Docs / Performance Optimization / Tune PHP-FPM Traffic Patterns

Tune PHP-FPM Traffic Patterns

By Admin · Mar 15, 2026 · Updated Apr 23, 2026 · 316 views · 5 min read

PHP-FPM (FastCGI Process Manager) is the backbone of PHP web applications. A misconfigured pool either wastes memory with idle processes or bottlenecks under load with too few workers. This guide shows how to analyze your traffic patterns and tune PHP-FPM accordingly, covering process manager modes, memory calculations, and monitoring strategies.

Understanding Process Manager Modes

PHP-FPM offers three process management modes, each suited to different scenarios:

Static Mode

; Best for: dedicated servers with consistent traffic
pm = static
pm.max_children = 50
; All workers are always running — no spawn overhead under load

Static mode pre-forks a fixed number of workers. Ideal when your server primarily runs PHP and has predictable traffic. The trade-off is memory usage — all workers consume RAM even when idle.

Dynamic Mode (Default)

; Best for: most VPS deployments with variable traffic
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500

Dynamic mode scales workers between min and max spare. Good general-purpose choice, but can cause latency spikes when spawning new workers during traffic surges.

Ondemand Mode

; Best for: low-traffic sites or shared hosting
pm = ondemand
pm.max_children = 30
pm.process_idle_timeout = 10s
pm.max_requests = 500

Ondemand mode starts workers only when requests arrive and kills them after idle timeout. Saves memory on quiet sites but adds latency for the first request after idle period.

Calculating pm.max_children

The most critical setting. Too high and you run out of RAM; too low and requests queue.

# Step 1: Find average memory per PHP-FPM worker
ps --no-headers -o rss -C php-fpm | awk '{ sum += $1; n++ } END { print sum/n/1024 " MB average per worker" }'

# Typical results:
# WordPress: 40-60 MB per worker
# Laravel: 30-50 MB per worker
# Magento: 80-120 MB per worker

# Step 2: Calculate max_children
# Formula: (Total RAM - OS/DB/other services) / Average worker memory
# Example: 4GB VPS, 1GB for OS/MySQL, 60MB per WordPress worker
# (4096 - 1024) / 60 = 51 workers
# Round down to 50 for safety margin

Memory Guard with pm.max_requests

; Recycle workers after N requests to prevent memory leaks
pm.max_requests = 500

; For applications with known memory leaks, set lower
pm.max_requests = 200

Pool Isolation

Run separate pools for different applications to prevent one from affecting another:

; /etc/php/8.3/fpm/pool.d/wordpress.conf
[wordpress]
user = www-data
group = www-data
listen = /run/php/php-fpm-wordpress.sock
pm = dynamic
pm.max_children = 30
pm.start_servers = 8
pm.min_spare_servers = 4
pm.max_spare_servers = 16
pm.max_requests = 500
php_admin_value[memory_limit] = 256M
php_admin_value[max_execution_time] = 30

; /etc/php/8.3/fpm/pool.d/api.conf
[api]
user = www-data
group = www-data
listen = /run/php/php-fpm-api.sock
pm = static
pm.max_children = 20
pm.max_requests = 1000
php_admin_value[memory_limit] = 128M
php_admin_value[max_execution_time] = 10

Monitoring PHP-FPM Status

; Enable status page in pool config
pm.status_path = /fpm-status

; Nginx config to expose it
location /fpm-status {
    access_log off;
    allow 127.0.0.1;
    deny all;
    fastcgi_pass unix:/run/php/php-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}
# Check status
curl -s http://localhost/fpm-status

# Key metrics:
# active processes — currently handling requests
# idle processes — waiting for requests
# listen queue — requests waiting for a free worker (should be 0!)
# max listen queue — historical peak queue length
# max active processes — peak concurrent workers used

# JSON format for monitoring tools
curl -s "http://localhost/fpm-status?json"

Alerting on Queue Buildup

#!/bin/bash
# /usr/local/bin/check-fpm.sh
QUEUE=$(curl -s "http://localhost/fpm-status?json" | jq '.["listen queue"]')
if [ "$QUEUE" -gt 0 ]; then
    echo "WARNING: PHP-FPM listen queue at $QUEUE — consider increasing pm.max_children"
    # Send alert
fi

Slow Request Logging

; Log requests taking longer than 5 seconds
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slow.log

; Also set hard timeout to kill stuck workers
request_terminate_timeout = 60s

The slow log captures stack traces of running PHP code when the timeout is hit, making it invaluable for debugging performance issues.

OPcache Configuration

OPcache works hand-in-hand with PHP-FPM. Misconfigured OPcache negates FPM tuning benefits:

; php.ini OPcache settings for production
opcache.enable = 1
opcache.memory_consumption = 256
opcache.interned_strings_buffer = 32
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 0  ; Don't check for file changes (deploy needs restart)
opcache.revalidate_freq = 0
opcache.save_comments = 1  ; Required for some frameworks (Laravel, Doctrine)

Traffic Pattern Analysis

# Analyze request patterns from access logs
# Requests per second over the last hour
awk -v d="$(date -d '1 hour ago' '+%d/%b/%Y:%H')" '$4 ~ d' /var/log/nginx/access.log | \
    awk '{print $4}' | cut -d: -f2,3 | sort | uniq -c | sort -rn | head -20

# Peak concurrent PHP requests (from FPM status over time)
while true; do
    curl -s "http://localhost/fpm-status?json" | \
        jq -r '"[" + (now|strftime("%H:%M:%S")) + "] Active: " + (.["active processes"]|tostring) + " Queue: " + (.["listen queue"]|tostring)'
    sleep 5
done

Tuning Workflow

  1. Start with pm = dynamic and conservative values.
  2. Monitor with pm.status_path for a week under normal traffic.
  3. Check: if listen queue > 0 frequently, increase pm.max_children.
  4. Check: if idle processes is consistently near max_spare_servers, your min is too low.
  5. Check: if max active processes equals pm.max_children, you hit the ceiling — increase it or switch to static.
  6. Verify total memory stays within budget: max_children * avg_worker_mb < available_ram.

Summary

PHP-FPM tuning is fundamentally a memory budgeting exercise. Measure your average worker memory, allocate your available RAM across the right number of workers, and monitor queue length to confirm you have enough capacity. Use pool isolation for multi-application servers, enable slow logging to catch performance regressions, and always pair FPM tuning with OPcache optimization for maximum benefit.

Was this article helpful?