When running PHP-FPM in dynamic mode (pm = dynamic
), the process manager dynamically adjusts the number of worker processes based on three key directives:
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 30
pm.max_children = 50
Let's clarify some confusing terms:
- Server/Worker Process: A PHP-FPM process that handles requests (often called "workers")
- Child Process: Same as worker process (terminology varies in docs)
- 1:1 Relationship: Each worker handles one request at a time
For a 4-core server with 8GB RAM:
; Recommended starting configuration
pm = dynamic
pm.max_children = 80
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 30
pm.process_idle_timeout = 10s
Calculation methodology:
max_children = (Total RAM - Other services) / Average PHP process size
start_servers = (max_children * 0.25)
min_spare_servers = (max_children * 0.125)
max_spare_servers = (max_children * 0.375)
Check current FPM status:
# Get current FPM status
sudo systemctl status php-fpm
# Alternatively for more details:
sudo ps aux | grep php-fpm | wc -l
- Monitor memory usage per process:
ps -ylC php-fpm --sort:rss
- Adjust
pm.process_idle_timeout
based on traffic patterns - For burst traffic, temporarily increase
start_servers
- Never set
max_spare_servers
higher thanmax_children
Mistakes I've seen in production:
; Bad configuration example
pm.max_children = 200 ; Too high for available RAM
pm.start_servers = 50 ; Causes unnecessary memory usage
pm.min_spare_servers = 5 ; Too low for production traffic spikes
When running PHP-FPM in dynamic mode (pm = dynamic
), three critical parameters control your process pool:
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 30
pm.max_children = 50
"Servers" vs "Children": In PHP-FPM terminology, each "server" is a PHP worker process. These are not directly equivalent to CPU cores, but rather to concurrent PHP execution contexts.
Process Lifecycle: PHP-FPM maintains a pool of worker processes:
- At startup: Creates
start_servers
workers - During low traffic: Keeps between
min_spare_servers
andmax_spare_servers
idle workers - Under load: Spawns new workers up to
max_children
Here's a realistic configuration example for a 4-core server with 8GB RAM:
pm = dynamic
pm.max_children = 100
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 30
pm.process_idle_timeout = 10s
Calculation methodology:
max_children = (Total RAM - System RAM) / Avg PHP process size
start_servers = (max_children * 0.2)
min_spare_servers = (max_children * 0.1)
max_spare_servers = (max_children * 0.3)
Use these commands to monitor your PHP-FPM status:
# Get current process count
ps aux | grep php-fpm | wc -l
# Check pool status (requires status path configured)
curl http://localhost/status
Sample output interpretation:
pool: www
process manager: dynamic
start time: 01/Jan/2022:00:00:00 +0000
start since: 12345
accepted conn: 67890
listen queue: 0
max listen queue: 1
listen queue len: 128
idle processes: 15
active processes: 5
total processes: 20
max active processes: 25
max children reached: 0
For high-traffic WordPress sites, consider these additional parameters:
pm.max_requests = 500
request_terminate_timeout = 30s
request_slowlog_timeout = 5s
Always test configurations with:
sudo php-fpm -t
sudo systemctl restart php-fpm
Problem: max children reached
appears in logs
Solution: Either increase max_children
or optimize your PHP code to reduce memory usage
Problem: High CPU usage with many idle processes
Solution: Reduce max_spare_servers
and adjust process_idle_timeout