The combination of Nginx and PHP-FPM is the gold standard for PHP application hosting. Properly tuning this stack can handle thousands of requests per second. This guide covers optimal configuration for both Nginx and PHP-FPM.
PHP-FPM Pool Configuration
# /etc/php/8.3/fpm/pool.d/www.conf
[www]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
# Process management
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 1000
# Status page (for monitoring)
pm.status_path = /fpm-status
ping.path = /fpm-ping
# Logging
access.log = /var/log/php-fpm/access.log
slowlog = /var/log/php-fpm/slow.log
request_slowlog_timeout = 5s
request_terminate_timeout = 30sCalculating max_children
# Formula: max_children = (Available RAM - OS/Other services) / Average PHP process size
# Check average PHP-FPM process size:
ps aux | grep php-fpm | awk "{sum += \$6; count++} END {print sum/count/1024 \"MB\"}"
# Typical: 30-50MB per process
# Example calculation:
# Server RAM: 8GB
# OS + MySQL + Nginx: ~2GB
# Available for PHP: 6GB = 6144MB
# Average PHP process: 40MB
# max_children = 6144 / 40 = 153
# Set to 150 (leave some headroom)Nginx FastCGI Configuration
# /etc/nginx/conf.d/php-fpm.conf
# FastCGI cache (optional, significant performance boost)
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=PHPCACHE:10m max_size=1g inactive=60m;
server {
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# Buffer settings
fastcgi_buffer_size 128k;
fastcgi_buffers 256 16k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
# Timeouts
fastcgi_connect_timeout 5s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 60s;
# Cache (for cacheable pages)
fastcgi_cache PHPCACHE;
fastcgi_cache_valid 200 10m;
fastcgi_cache_methods GET HEAD;
add_header X-Cache-Status $upstream_cache_status;
}
}PHP.ini Optimization
# /etc/php/8.3/fpm/php.ini
# OPcache (CRITICAL for performance)
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0 # Set to 1 for development
opcache.jit=1255
opcache.jit_buffer_size=128M
# Memory and execution
memory_limit=256M
max_execution_time=30
max_input_vars=5000
# Upload limits
upload_max_filesize=64M
post_max_size=64M
# Session
session.gc_maxlifetime=86400Monitoring PHP-FPM
# Enable status page in Nginx
location /fpm-status {
access_log off;
allow 127.0.0.1;
deny all;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Check status
curl http://localhost/fpm-status
# Key metrics:
# active processes — Currently handling requests
# idle processes — Waiting for requests
# listen queue — Requests waiting for a free process (should be 0)
# max children reached — Pool exhaustion events (increase max_children)