A PHP-FPM warning that pm.max_children has been reached means the configured process limit was encountered. It does not, by itself, prove that adding workers is safe or that the pool is still saturated. Check current queueing, active workers, request duration and available resources before changing capacity.1, 2
The key question is why workers remain occupied. Legitimate concurrency, slow application code, database waits and external dependencies can all produce pressure at the same FPM limit. More workers may help one case while increasing contention in another.
This article uses the official PHP manual text, excluding user-contributed comments. Protect status and diagnostic access according to the deployment.
Confirm the pool and process-manager mode
Record the PHP version, pool name, listener, effective configuration and service start time. On a host with several pools, a warning from one pool does not establish that another application's workers are exhausted.
The manual defines three modes. static creates the configured number of children. dynamic adjusts workers using its starting and spare-worker settings within the maximum. ondemand starts workers when requested and removes idle processes according to its idle timeout.1
pm.max_children is the fixed count for static mode and the maximum for dynamic and ondemand modes. It limits simultaneous requests served by that pool. These modes do not have identical startup or idle-capacity behaviour.1
Read the effective configuration, including included files, rather than changing a value in whichever file is easiest to find. Preserve the original settings and the reason for any later adjustment.
Read current state and historical peaks separately
The status page distinguishes listen queue, the requests currently waiting for a free process, from max listen queue, the highest observed queue. It also reports idle, active and total processes, along with pool start information.2
The max children reached value records whether the maximum was reached; a nonzero value is not a live measure of how many requests are waiting now. Compare it with the current queue, current active count and the incident interval.2
A hypothetical pool may have reached its maximum during a short earlier burst and now have idle workers and no queue. Another pool may have all workers active with a queue that persists. Those observations require different capacity decisions.
Retain start time when comparing cumulative values and maxima. A restart creates a new observation period; a lower counter after restart does not prove better performance. Never calculate a rate across a counter reset without accounting for it.
Make status collection safe and available
The PHP manual warns that the status page reveals request URLs and resource information and should be restricted to internal requests or known client addresses. An unauthenticated public status page is not an acceptable shortcut to diagnosis.2
If an existing protected endpoint is available, use that approved path. If one must be added, treat it as a reviewed configuration change with access controls and a clear owner. Do not expose full process details merely because a monitoring tool requests them.
The manual documents pm.status_listen as an independent status listener that can remain useful when the main pool is busy. Confirm support in the installed PHP build and the intended routing before designing around it.1
A failed status request through a saturated path is ambiguous: the evidence channel itself may be queued. Record that limitation instead of concluding the pool has crashed. Status collection must not become a repeated source of extra demand during an incident.
Determine what occupies the workers
Correlate pool state with application and web-server request records over the same interval. Identify which routes or jobs are slow, how often they arrive, and whether their latency changed before the queue grew.
The full status output describes per-process request information, but fields have specific semantics. The manual describes request duration in microseconds and notes that a front-controller application may report the same script path for many different routes.2
Do not treat a common index.php path as proof that every worker is handling identical business work. Use the application's own correlation and route information where available, with sensitive values removed.
The FPM slowlog can record a PHP backtrace when a request exceeds request_slowlog_timeout. This can help locate the waiting or executing code, but enabling it is a diagnostic change with log volume and access considerations.1
A stack shows where the request was sampled, not automatically the initiating fault. A database call in many stacks should lead to correlation with database waits; it should not be labelled an FPM defect merely because FPM exposes the symptom.
Build a memory budget from measured process behaviour
Adding workers increases the potential memory demand of the pool. Measure representative busy-worker behaviour and the host or container's effective limit before deciding how many additional processes it can sustain.
On Linux, resident set size includes resident pages mapped by each process. Shared mappings can therefore appear in several workers' RSS values. The smaps documentation distinguishes RSS, proportional set size and private/shared pages; PSS apportions shared mappings.3
Multiplying a single worker's RSS by the proposed count is not a precise model of total pool memory. It can double-count shared pages while also missing variation between request types and peaks over a worker's lifetime.
As an editorial budgeting method, account separately for shared memory, representative private-worker demand, other processes, operating-system needs and reserve for observed bursts. Validate the resulting estimate against measured aggregate memory under an approved workload.
A PHP script's memory limit is not sufficient evidence for the whole process pool's physical footprint. Use process and system measurements rather than treating one configuration value as an exact per-worker allocation.
Check the downstream limit before increasing concurrency
If workers mostly wait on a constrained database or external service, more PHP concurrency can increase outstanding work at that dependency. The investigation should establish whether additional workers can actually complete more requests.
Compare queueing with CPU, memory pressure and dependency latency. Low CPU does not prove spare end-to-end capacity when workers are blocked elsewhere. High CPU with growing queueing suggests a different constraint from idle processors waiting on I/O.
A change may be justified when measured demand exceeds an otherwise healthy pool's capacity and resources are available. State the intended effect, the maximum acceptable resource use and the conditions for reverting it.
Do not use a universal “workers per CPU” multiplier. Request mix and dependency behaviour determine service time, while memory and downstream limits constrain safe concurrency.
Recycling and backlog settings solve different problems
The manual defines pm.max_requests as the number of requests a child serves before respawning and notes its use for working around leaks in third-party libraries. It is not the simultaneous-request limit.1
Recycling may bound a known accumulation while a root-cause repair is pursued, but it does not prove the leak is fixed. Similarly, a larger backlog creates more waiting capacity; it does not itself make occupied workers finish sooner.
Keep these changes separate so their effects can be measured. A large bundle of worker, timeout and queue changes makes it difficult to identify what improved latency or introduced failures.
Verify throughput and resource cost together
After an approved change, compare current queueing, latency percentiles, successful completion rate, worker activity, memory and dependency behaviour under representative load. Include slow and error paths, not only a quick homepage request.
The desired result is sustained useful throughput within resource limits. A quiet warning log with worse latency or memory pressure is not success. Record remaining uncertainty when the original traffic pattern could not be reproduced.
Learning notes
- A historical maximum warning is not the same as current queueing.
- Worker limits must fit request duration, memory and downstream capacity.
- Status and slowlog evidence need protected, bounded access.
- More waiting capacity or frequent recycling does not fix slow application work.
Acceptance checklist
- [ ] Current queueing is separated from historical maxima and warnings.
- [ ] Worker demand, memory reserve and downstream capacity support the selected limit.
- [ ] Protected diagnostics identify slow or blocked request paths.
- [ ] Representative load meets throughput and latency limits without added memory/dependency pressure.
References
- PHP: Configuration - Manual. Accessed 2026-11-01.
- PHP: Status Page - Manual. Accessed 2026-11-01.
- proc_pid_smaps(5) - Linux manual page. Accessed 2026-11-01.