PHP-FPM Tuning
Learning Focus
Leave this lesson with a working understanding of php-fpm tuning that you can apply immediately in production.
Complete reference for PHP-FPM configuration with Nginx.
File Paths
# Pool configs (Debian/Ubuntu)
/etc/php/8.4/fpm/pool.d/www.conf # Default pool
/etc/php/8.4/fpm/pool.d/site2.conf # Per-site pool
# php.ini
/etc/php/8.4/fpm/php.ini # FPM php.ini (Debian)
/etc/php/8.4/fpm/conf.d/ # Extra ini files
# Socket (created at runtime)
/run/php/php8.4-fpm.sock
# Logs
/var/log/php8.4-fpm.log
/var/log/php8.4-fpm-slow.log
Pool Config — Annotated
/etc/php/8.4/fpm/pool.d/www.conf
[www]
user = www-data
group = www-data
; Socket (faster than TCP)
listen = /run/php/php8.4-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
; PM mode: static | dynamic | ondemand
pm = dynamic
pm.max_children = 20 ; Hard cap on workers
pm.start_servers = 5 ; Workers at startup
pm.min_spare_servers = 5 ; Min idle workers
pm.max_spare_servers = 10 ; Max idle workers (kill extras)
pm.max_requests = 500 ; Requests per worker before restart
; Status endpoint
pm.status_path = /fpm-status
ping.path = /fpm-ping
ping.response = pong
; Slow request logging
slowlog = /var/log/php8.4-fpm-slow.log
request_slowlog_timeout = 5s
; php.ini overrides per pool
php_admin_value[error_log] = /var/log/php8.4-fpm-error.log
php_admin_flag[log_errors] = on
php_value[session.save_path] = /var/lib/php/sessions/
PM Mode Comparison
| Mode | Workers | RAM | Best For |
|---|---|---|---|
static | Fixed max_children | High | High-load dedicated servers |
dynamic | Scales min→max | Medium | Most production sites |
ondemand | Spawn on request, kill on idle | Lowest | Low-traffic / many sites |
Size max_children
# Formula: available_RAM / avg_worker_RSS
# Check current avg worker memory
ps aux | grep php-fpm | grep -v grep | awk '{print $6}' | \
awk '{sum+=$1; n++} END {printf "Avg: %.0f KB (%.0f MB)\n", sum/n, sum/n/1024}'
# Example: 2 GB available, 30 MB per worker → max_children = 68
php.ini — Production Settings
/etc/php/8.4/fpm/php.ini
; Memory
memory_limit = 256M ; 512M for WooCommerce/heavy apps
; Execution
max_execution_time = 60
max_input_time = 60
max_input_vars = 5000 ; WordPress needs 5000+
; Uploads
upload_max_filesize = 64M
post_max_size = 64M
; Error handling (production)
display_errors = Off
log_errors = On
error_log = /var/log/php8.4-fpm-error.log
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED
; Security
expose_php = Off ; Remove X-Powered-By header
session.cookie_secure = 1
session.cookie_httponly = 1
session.cookie_samesite = Strict
; Timezone
date.timezone = UTC
OPcache — Must Enable
php.ini or conf.d/opcache.ini
opcache.enable = 1
opcache.enable_cli = 0
opcache.memory_consumption = 128 ; MB
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 10000
opcache.revalidate_freq = 0 ; 0 = never check disk (production)
; For development: set to 2
; JIT (PHP 8.x)
opcache.jit_buffer_size = 64M
opcache.jit = tracing
# Verify OPcache is enabled
php8.4 -r "var_dump(opcache_get_status(false)['opcache_enabled']);"
Multiple PHP Versions
# Install both PHP 8.4 and 8.1
sudo apt install php8.4-fpm php8.1-fpm
# Each version gets its own socket
ls /run/php/
# php8.4-fpm.sock
# php8.1-fpm.sock
# In Nginx — use per-site in location block
# PHP 8.4 site:
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
# PHP 8.1 site (different server block):
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
Diagnostics
# Status
sudo systemctl status php8.4-fpm
# Restart after php.ini change
sudo systemctl restart php8.4-fpm
# Reload after pool config change
sudo systemctl reload php8.4-fpm
# Worker count
ps aux | grep php-fpm | grep -v grep | wc -l
# Total FPM memory
ps aux | grep php-fpm | grep -v grep | awk '{sum+=$6} END {printf "%.0f MB\n", sum/1024}'
# Slow request log
sudo tail -f /var/log/php8.4-fpm-slow.log
# Error log
sudo tail -f /var/log/php8.4-fpm.log
# Which php.ini is loaded
php8.4 --ini | grep "Loaded Configuration"
# Verify OPcache
php8.4 -r "echo opcache_get_status() ? 'OPcache ON' : 'OFF';"
# Test PHP directly
php8.4 -r "echo 'PHP OK: '.PHP_VERSION;"
Common PHP-FPM Issues
| Problem | Symptom | Fix |
|---|---|---|
| FPM not running | 502 Bad Gateway | systemctl start php8.4-fpm |
| Socket doesn't exist | 502 + "No such file" | Verify FPM is running; check socket path |
| Permission denied on socket | 502 + "Permission denied" | Set listen.owner = www-data in pool config |
| Wrong socket in Nginx | 502 | ls /run/php/ and match exact filename |
| PHP file downloads | Raw PHP shown | Missing location ~ \.php$ block in Nginx |
memory_limit too low | 500 error | Increase memory_limit in php.ini |
| OPcache stale in dev | PHP changes not reflecting | Set opcache.revalidate_freq=2 for dev |
| Session not persisting | Logout between requests | Check session.save_path is writable by www-data |