High-Performance Web Architecture: Nginx and PHP-FPM Tuning Guide
Optimizing Nginx and PHP-FPM for high-concurrency production workloads requires tuning Linux kernel network queues, process scheduling models, memory consumption, Unix socket communication, and FastCGI microcaching.
⚡ Quick Dive
Key Tuning Directives Summary
| Subsystem | Directive | Recommended Production Value | Purpose |
|---|---|---|---|
| Nginx | worker_processes |
auto (1 per physical CPU core) |
Eliminates CPU context switching |
| Nginx | worker_connections |
65535 |
Max simultaneous connections per worker |
| Nginx | multi_accept |
on |
Worker accepts all new connections at once |
| Nginx | keepalive_timeout |
15s - 30s |
Reuses open TCP/TLS connections |
| PHP-FPM | pm (Process Manager) |
static (Dedicated servers) / dynamic |
Process allocation lifecycle |
| PHP-FPM | pm.max_children |
(Total RAM - 1GB) / Avg PHP RAM |
Prevents OOM crashes under high load |
| IPC | listen |
unix:/run/php/php-fpm.sock |
15-20% faster than TCP loopback |
Memory Sizing Formula for pm.max_children
$$\text{pm.max_children} = \frac{\text{Total Available RAM (MB)} - \text{OS/DB Overhead (1024 MB)}}{\text{Average RAM per PHP Process (MB)}}$$
Example: On a 16GB RAM server where average PHP process consumes 60MB: $$\text{pm.max_children} = \frac{16384 - 1024}{60} \approx 256$$
📖 Extended Guide
1. High-Performance Architecture Blueprint
Client Requests ──► Nginx (Event-Driven / Non-Blocking)
│ (FastCGI Protocol over Unix Domain Socket)
▼
PHP-FPM Master Process
├── Worker Pool Process 1 (Persistent in RAM)
├── Worker Pool Process 2
└── Worker Pool Process N
2. Production nginx.conf Configuration
user www-data;
worker_processes auto;
worker_rlimit_nofile 65535; # Matches OS ulimit -n
events {
worker_connections 65535;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Performance I/O
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 30;
types_hash_max_size 2048;
server_tokens off; # Security: Hide Nginx version
# FastCGI Microcache Zone
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=MICROCACHE:100m inactive=60m max_size=1g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
# Gzip Compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
include /etc/nginx/conf.d/*.conf;
}
3. Production PHP-FPM Pool Configuration (/etc/php/8.2/fpm/pool.d/www.conf)
[www]
user = www-data
group = www-data
; Use Unix Domain Socket for maximum throughput
listen = /run/php/php8.2-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
listen.backlog = 65535
; Process Manager Settings
pm = static
pm.max_children = 256
pm.max_requests = 1000 ; Recycle workers periodically to prevent PHP memory leaks
; Timeouts & Status
request_terminate_timeout = 60s
pm.status_path = /fpm-status
ping.path = /fpm-ping
4. FastCGI Microcaching (Serving 10,000+ req/sec)
Cache dynamic PHP responses in memory for 1 to 5 seconds to absorb traffic spikes without hitting PHP-FPM:
server {
listen 80;
server_name example.com;
root /var/www/html;
set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($http_cookie ~* "comment_author|wordpress_logged_in") { set $skip_cache 1; }
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# Microcache settings
fastcgi_cache MICROCACHE;
fastcgi_cache_valid 200 301 302 5s;
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-Cache-Status $upstream_cache_status;
}
}