skip to content

What do the PHP-FPM status page's active processes, listen queue and max children reached fields tell you about a pool?

level: middleimportance: should knowfreq 42%

answer

  1. pm.status_path, off by default
  2. active equals total means saturated
  3. listen queue: connections waiting now
  4. max children reached: dynamic and ondemand only
  5. ?json, ?full, ?openmetrics

basics

~20 s

Active processes counts workers busy right now; listen queue counts connections waiting for a free worker; max children reached counts how often the pool wanted to grow past pm.max_children. Busy workers at the cap plus a non-zero queue means a saturated pool.

solid answer

~50 s

The status page is enabled per pool with `pm.status_path` (unset by default) and served by FPM through the web server. `active processes` is how many workers are handling a request right now, `idle processes` how many wait, `total processes` their sum; `max active processes` is the high-water mark since start. `listen queue` is how many connections are waiting in the socket backlog for a free worker, `max listen queue` its peak and `listen queue len` the backlog's size. `max children reached` counts the times the process manager wanted another worker but was at `pm.max_children`; it only moves for `dynamic` and `ondemand`. Active equal to total at the cap, a non-zero queue and a rising max-children counter together mean requests are waiting. Two traps: the listen-queue figures come from TCP socket statistics, so a Unix-socket pool reports 0; and the page itself needs a free worker unless `pm.status_listen` is set.

code

ini · 5 lines
ini
[app]
listen = 127.0.0.1:9000
pm.status_path = /fpm-status
; answer status requests even when all app workers are busy (PHP 8.0+)
pm.status_listen = 127.0.0.1:9001

go deeper

for a junior

Recall that pm.status_path turns on a status page showing how many workers are busy and how many requests are waiting.

for a middle

Explain each key field — active, idle, total, listen queue and its length, max children reached — and how they combine to show saturation.

for a senior

Know the traps: no queue figures for Unix sockets, max children reached frozen for static pools, cumulative counters, and a status page blocked by its own busy pool.

for a principal

Decide which FPM metrics feed alerts and capacity reviews, and how status access is kept private across environments.

## Enabling the page Each pool can expose a status page by setting **`pm.status_path`**, for example `/status` (the value must start with `/`, and it is not set by default). FPM answers it itself when a request arrives whose script name matches, so the web server must route that path to the pool — and should restrict it to monitoring hosts, because the full view lists request URIs and scripts. The output format is chosen with the query string: - plain text by default; `?json`, `?xml`, `?html`; - `?openmetrics` for metrics scrapers that read the OpenMetrics text format (added in PHP 8.1); - `&full` (or `?full`) adds one block per worker process. ## The pool-level fields | Field | Meaning | |---|---| | `pool`, `process manager` | Pool name and `static`/`dynamic`/`ondemand` | | `start time`, `start since` | When FPM started, seconds since | | `accepted conn` | Requests accepted by the pool since start | | `listen queue` | Connections **currently** waiting for a free worker | | `max listen queue` | Highest `listen queue` since start | | `listen queue len` | Size of the socket's pending-connection queue | | `idle processes` / `active processes` / `total processes` | Workers waiting, busy, and both | | `max active processes` | Highest `active processes` since start | | `max children reached` | Times the process limit was hit when trying to start more workers | | `slow requests` | Requests that exceeded `request_slowlog_timeout` | | `memory peak` | Memory usage peak since start (field added in PHP 8.4) | ## Reading them together - **Healthy:** `idle processes` above zero most of the time, `listen queue` at 0. - **Saturated:** `active processes` equals `total processes` and `total processes` equals `pm.max_children`; `listen queue` above 0 means requests are waiting **now**, and a `max listen queue` above 0 means they have waited at some point since start. - **Hitting the cap repeatedly:** `max children reached` rising between two scrapes. It counts events, not duration, and it is only maintained for `dynamic` and `ondemand` pools; a `static` pool keeps it at 0 even when saturated. - **Queue near its size:** `listen queue` approaching `listen queue len` means new connections are about to fail. - **Slow code, not too few workers:** `slow requests` rising while CPU is idle points at requests waiting on something. Most counters (`accepted conn`, `max …`, `slow requests`) are cumulative since FPM started, so monitoring systems should graph their **rate of change**, not the absolute value. A saturated pool in the default text format looks like this: ```text pool: app process manager: dynamic listen queue: 37 max listen queue: 211 listen queue len: 511 idle processes: 0 active processes: 40 total processes: 40 max children reached: 18 ``` ## Two traps 1. **Unix sockets report no queue.** FPM measures `listen queue` and `max listen queue` from the kernel's TCP socket information (on Linux, `TCP_INFO`), and its maintenance loop only does so for TCP listeners. A pool listening on a Unix socket shows `0` even while connections pile up. For queue visibility, either listen on TCP or measure at the web server. 2. **The page is served by a worker.** A status request goes through the pool like any other request, so when every worker is busy the status page waits in the same queue — exactly when you need it. `pm.status_listen` (PHP 8.0+) creates a separate hidden pool on another address that answers status requests independently. ## The per-process view With `full`, each worker shows its `pid`, `state` (`Idle`, `Reading headers`, `Running`, `Finishing`, `Ending`), `requests` served, and for the current or last request its `request duration` (in microseconds), `request method`, `request URI`, `script`, `last request cpu` and `last request memory`. Several workers in `Running` on the same URI with long durations is often the fastest way to find the page that is holding the pool. ## Using it in practice - Scrape `?json` or `?openmetrics` every few seconds and alert on `listen queue > 0` sustained, and on the rate of `max children reached`. - Keep the path private: allow only localhost or the monitoring network at the web server. - Pair it with the FPM error log, which records the matching warnings, and with the slow log for what the busy workers are doing.

  • A pool listening on a Unix socket always shows listen queue 0, even under heavy load. Is nothing queuing?
    Not necessarily. PHP-FPM reads the queue from TCP socket statistics and only for TCP listeners, so a Unix-socket pool reports 0 regardless. Use `active processes` against `total processes`, the `max children reached` counter for dynamic pools, and response times at the web server, or switch the pool to a private TCP address if you need the queue figure.
  • Why can the status page itself time out exactly when the pool is overloaded?
    The status request is handled by a pool worker like any other request. If all workers are busy, it waits in the same listen queue. Setting `pm.status_listen` (PHP 8.0+) gives status requests their own small hidden pool on another address, so they are answered even while the main pool is saturated.

saying these in an interview costs you the question

  • max children reached counts how many workers exist right now
  • A static pool's max children reached rises when it saturates
  • listen queue 0 on a Unix-socket pool proves nothing is waiting
  • The status page is on by default at /status
  • accepted conn is the number of requests per second