PHP-FPM (FastCGI Process Manager) is the PHP runtime behind most production WordPress servers. Its pool configuration controls how many worker processes handle requests, how they’re managed, and how to detect slow scripts — getting these settings right prevents both OOM crashes and request queuing under load.
Problem: A WordPress site under load shows 502 Bad Gateway errors — PHP-FPM logs show worker exhaustion, but the default pool configuration was never tuned for the site's actual traffic pattern.
Solution: Tune PHP-FPM by switching to pm = dynamic and setting pm.max_children based on available RAM divided by average worker memory usage (ps --no-headers -o rss -C php-fpm | awk '{ sum += $1 } END { print sum/NR }'). Set pm.max_requests = 500 to recycle workers that accumulate memory leaks, and enable the slow log with request_slowlog_timeout = 5s.
The examples below show the three PHP-FPM process management modes with recommended WordPress settings, enable the slow log to find blocking PHP scripts, and configure per-pool limits for a multisite environment.
# /etc/php/8.2/fpm/pool.d/wordpress.conf
[wordpress]
user = www-data
group = www-data
listen = /run/php/php8.2-fpm-wordpress.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
# ── PROCESS MANAGEMENT MODE ──
# dynamic: spawns/kills workers based on demand (best for most sites)
# static: fixed number of workers (predictable, high-traffic sites)
# ondemand: starts workers only when requests arrive (low-traffic/idle sites)
pm = dynamic
pm.max_children = 20 # max concurrent PHP processes
pm.start_servers = 5 # workers spawned at startup
pm.min_spare_servers = 3 # min idle workers (spawn more if below)
pm.max_spare_servers = 8 # max idle workers (kill extras above this)
pm.max_requests = 500 # restart worker after N requests (prevents memory leaks)
# Memory budget check:
# pm.max_children = total_RAM_for_PHP / avg_worker_memory
# Check avg: ps --no-headers -o rss -C php-fpm8.2 | awk '{sum+=$1} END {print sum/NR/1024 " MB"}'
Enable the PHP-FPM slow log and status page:
# Continued: /etc/php/8.2/fpm/pool.d/wordpress.conf
# Slow log — records stack traces for requests taking over 2 seconds
slowlog = /var/log/php-fpm/wordpress-slow.log
request_slowlog_timeout = 2s
# Request timeout — kill workers stuck longer than this (prevents blocking)
request_terminate_timeout = 60s
# Status page — expose at /php-status (restrict in Nginx to internal IPs)
pm.status_path = /php-status
# Environment variables passed to PHP workers
env[HOSTNAME] = $HOSTNAME
env[WP_ENV] = production
# ── NGINX: restrict /php-status ──
# location = /php-status {
# fastcgi_pass unix:/run/php/php8.2-fpm-wordpress.sock;
# include fastcgi_params;
# allow 127.0.0.1;
# deny all;
# }
# Read the status page
curl http://127.0.0.1/php-status
# Output:
# pool: wordpress
# process manager: dynamic
# start time: ...
# accepted conn: 48291
# listen queue: 0 <- non-zero means workers are overwhelmed
# max listen queue: 2
# active processes: 3
# idle processes: 5
# Reload FPM config without dropping connections
systemctl reload php8.2-fpm
# View the slow log live
tail -f /var/log/php-fpm/wordpress-slow.log
NOTE: A non-zero listen queue in the status page means all workers are busy and new requests are waiting — increase pm.max_children if you have available RAM, or profile the slow log to find and fix the blocking code paths that are keeping workers occupied.