Skip to main content

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

ModeWorkersRAMBest For
staticFixed max_childrenHighHigh-load dedicated servers
dynamicScales min→maxMediumMost production sites
ondemandSpawn on request, kill on idleLowestLow-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

ProblemSymptomFix
FPM not running502 Bad Gatewaysystemctl start php8.4-fpm
Socket doesn't exist502 + "No such file"Verify FPM is running; check socket path
Permission denied on socket502 + "Permission denied"Set listen.owner = www-data in pool config
Wrong socket in Nginx502ls /run/php/ and match exact filename
PHP file downloadsRaw PHP shownMissing location ~ \.php$ block in Nginx
memory_limit too low500 errorIncrease memory_limit in php.ini
OPcache stale in devPHP changes not reflectingSet opcache.revalidate_freq=2 for dev
Session not persistingLogout between requestsCheck session.save_path is writable by www-data