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;
    }
}