PHP-FPMInfrastructureLinux 7 min read

PHP-FPM monitoring: the metrics that actually matter

Most PHP teams don't monitor PHP-FPM until it causes an outage. Here's what to watch, why it matters, and how to set up alerts before things break.


PHP-FPM (FastCGI Process Manager) is the component that runs your PHP code. It manages a pool of worker processes, each of which handles one request at a time. When all workers are busy, new requests queue up. When the queue is full, PHP-FPM starts rejecting connections.

Most teams discover PHP-FPM saturation when users start complaining about 502 or 504 errors — not from a monitoring dashboard. This guide explains what to monitor, what the numbers mean, and how to set up proactive alerts.

How PHP-FPM works (the short version)

PHP-FPM runs in one of three process management modes:

For production Laravel/Symfony apps, dynamic mode with sensible min/max values is usually the right choice.

The PHP-FPM status page

PHP-FPM exposes a status endpoint that shows real-time pool metrics. Enable it in your pool config (/etc/php/8.2/fpm/pool.d/www.conf):

pm.status_path = /fpm-status

Expose it via nginx (keep it internal — don't expose to the public internet):

location = /fpm-status {
    allow 127.0.0.1;
    deny all;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}

Test it:

curl "http://127.0.0.1/fpm-status?json"

You'll see output like:

{
  "pool": "www",
  "process manager": "dynamic",
  "accepted conn": 1482943,
  "listen queue": 0,
  "max listen queue": 14,
  "listen queue len": 128,
  "idle processes": 3,
  "active processes": 2,
  "total processes": 5,
  "max active processes": 8,
  "max children reached": 0,
  "slow requests": 12
}

The metrics that matter — and what they mean

active processes / total processes

This is your utilization ratio. If active == total consistently, you've saturated the pool — new requests are queuing. The correct response is to either increase pm.max_children or add more servers.

⚠️ When all workers are busy, PHP-FPM doesn't process new requests — it queues them in the OS listen queue (up to listen.backlog connections). Once the queue is full, nginx returns a 502 to clients.

listen queue

The number of requests currently waiting for a free worker. This should always be 0 in normal operation. Any sustained non-zero value means your pool is saturated and users are experiencing latency spikes. If this hits listen.backlog (default 511 on Linux), connections start getting dropped.

max children reached

A counter that increments every time PHP-FPM tried to spawn a new worker but was already at pm.max_children. This is the single best early-warning indicator of saturation. Monitor this counter over time — if it's climbing, you need more workers or faster code.

slow requests

The count of requests that exceeded request_slowlog_timeout. Enable slow logging in your pool config:

slowlog = /var/log/php-fpm/www-slow.log
request_slowlog_timeout = 5s

Slow logs record the full PHP call stack at the moment a request exceeds the timeout — invaluable for finding slow functions without full APM.

Setting up PHP-FPM monitoring in MonitorKit

The MonitorKit server agent polls the PHP-FPM status endpoint and ships metrics to your dashboard every 30 seconds. Enable it in config.toml:

[phpfpm]
enabled = true
status_urls = ["http://127.0.0.1/fpm-status?json"]

# Multiple pools? List all status URLs:
# status_urls = [
#   "http://127.0.0.1/fpm-status-www?json",
#   "http://127.0.0.1/fpm-status-api?json"
# ]

Restart the agent:

systemctl restart monitorkit-agent

PHP-FPM metrics now appear in the host detail panel under the "PHP-FPM" tab. You'll see active vs idle workers, listen queue depth, and requests per second — updated every 30 seconds.

Alerts to set up

Create these alert rules in the MonitorKit dashboard for proactive PHP-FPM monitoring:

1. php-fpm service down

Use a Service Down alert rule targeting php-fpm. This fires within 60 seconds if the PHP-FPM systemd service stops running — before nginx starts returning 502s.

2. High CPU (worker saturation proxy)

CPU > 85% for 5 minutes often correlates with PHP-FPM saturation. Set a threshold alert on cpu_percent > 85.

3. High memory

PHP-FPM workers each use ~20–80MB of RAM depending on your app. If pm.max_children is too high and all workers spin up simultaneously, you can exhaust RAM and trigger OOM kills. Alert on memory_percent > 85.

Tuning PHP-FPM for production

How many workers should you run?

The classic formula:

pm.max_children = (available RAM) / (average PHP process RAM)

# Example: 4GB RAM, 50MB per PHP process
# pm.max_children = 4096 / 50 = ~80 (leave headroom for OS, MySQL, nginx)
# Safe value: 60

Find your average PHP process size:

ps aux | grep php-fpm | awk '{print $6}' | sort -n | tail -20

This shows RSS in KB. Divide by 1024 for MB.

Recommended dynamic mode settings for a typical Laravel app on 4GB RAM

pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500       ; recycle workers to prevent memory leaks

OPcache — don't skip this

OPcache compiles PHP files once and caches the bytecode. Without it, every request re-parses every file. Enable it in /etc/php/8.2/fpm/conf.d/10-opcache.ini:

opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.revalidate_freq=0        ; 0 = never in production (restart to reload)
opcache.validate_timestamps=0    ; 0 = fastest, disable file change checks

With OPcache enabled, a typical Laravel request drops from 80–150ms (without) to 5–30ms (with). That translates directly to higher PHP-FPM throughput with the same number of workers.

Reading a PHP-FPM saturation incident

Here's what a typical saturation event looks like in the metrics:

  1. Traffic spike — CPU climbs, active processes approach max_children
  2. listen queue > 0 — requests start backing up, user-perceived latency increases
  3. max children reached increments — PHP-FPM can't spawn more workers
  4. nginx 502s — listen queue is full, PHP-FPM rejects connections
  5. Alert fires — if you've set up a php-fpm service alert or CPU alert

Monitoring PHP-FPM turns step 5 into step 1 — you get notified before users do.

Related guides

Try MonitorKit free

Everything in this article — APM, server monitoring, alerts — in one tool. 14-day trial, no credit card.

Start free trial →