Payments are experiencing issues due to temporary restrictions in Russia. If your payment does not go through, please submit a support request.Our support team is available 24/7 — we are always here to help with hosting and server issues.We are now accepting requests for dedicated server rental and colocation services in our data center.Reminder: we recommend enabling backups for additional data protection.A new VPS/VDS lineup with NVMe storage and improved performance is now available.Maintenance work on some servers has been completed. All services are operating normally.
Article5 min readViews5

CPU Idle While PHP Requests Wait: How to View the PHP-FPM Queue

We examine a specific PHP-FPM pool during slowdowns: which metrics to compare, why an idle CPU doesn't rule out waiting, and what to do if the queue is empty. Includes a reproducible educational calculation.

Comments 0

Diagram showing request waiting before four PHP-FPM processes on a blue and orange background.
In this article

A product card takes several seconds to open, yet average CPU usage remains low. This alone does not prove the server has sufficient resources. A dynamic request may be waiting for a free PHP-FPM process; meanwhile, already occupied processes might be waiting for the database or an external service.

I would start with a more specific question: were requests in the queue for the exact PHP pool serving the store at the moment of the delay? Neither CPU percentage nor a single historical maximum value answers this on their own.

First, separate process waiting from code execution

A pool is a group of PHP-FPM processes. A free process accepts a request; a busy one handles the previous request. If no free processes exist, requests may wait before the pool. This waiting period differs from the time a process already holding a request spends executing application code.

The path of a dynamic request through the web server, the FPM queue, and the PHP process to the database or external API.
The diagram distinguishes between waiting for a free process and executing the request. It does not show the store's actual delays.

Average CPU usage across the server can mask the load on a specific core. The reverse situation is also possible: a PHP process is busy handling a request but spends most of its time waiting for a response from another system. Therefore, an idle processor does not equal an available pool.

The pm.max_children setting limits the number of requests served simultaneously. In static mode, it defines the number of processes; in dynamic mode and on-demand mode, it sets the upper limit. This is described in the PHP-FPM configuration documentation. These rules do not imply that the limit must be increased immediately.

What to ask the administrator during slowdowns

If the FPM status is already being collected by internal monitoring, retrieve the data from there. Otherwise, ask the administrator to prepare access restricted only to authorized internal requests. The status contains information about resources and requests; there is no need to expose it to all site visitors for verification purposes.

You can verify the meaning of the fields against the official FPM status page description. For a start, the following values are sufficient:

Field

How to use

pool

Check that the store pool is selected.

listen queue

Current wait time for a free process.

idle processes / active processes

How many processes are free and busy in this snapshot.

max listen queue

Historical maximum; not the current queue.

max children reached

Has the limit been reached? The old value does not date the current failure.

start time / start since

Detect a restart between measurements.

Save several snapshots during the slowdown and one after it. Record the time with the time zone, the affected store route, the response time, and any errors for that same interval. These data points belong to a single pool; after an FPM restart, its values are reset.

For a request to the administrator, the following phrasing is sufficient: 'During the slow loading of the product card, check the pool for this site: is there a current queue, are there still idle processes, and is the configured limit being reached? Save the metrics along with the response time so they can be compared on the same scale.' This is a request for observation, not for configuration changes.

Training check: why four processes do not always keep up

To verify the arithmetic, we performed a separate continuous flow calculation while preparing the article. It involves four processes, a uniform input of 10 requests per second, and an initial queue of zero. One request occupies a process for 0.5 seconds. The ideal throughput is then 4 / 0.5 = 8 requests per second, and the queue grows by 2 requests per second.

The calculation yielded 10 pending requests after 5 seconds and 20 after 10. In the second variant, we kept four processes and the same input but reduced the request processing time to 0.25 seconds. The calculated capacity became 16 requests per second, and the queue in this model did not grow.

Calculated queue graph: at a duration of 0.5 seconds, it grows from zero to twenty; at a duration of 0.25 seconds, it remains zero.
This is an educational continuous flow calculation. It is not a PHP-FPM benchmark, not a store test, and not a customer history.

The check can be repeated in Python 3. The following lines output two calculated queue lengths for each moment:

workers, incoming = 4, 10

for seconds in (0, 5, 10):

queues = [max(0, (incoming - workers / hold) * seconds) for hold in (0.5, 0.25)]

print(seconds, queues)

Calculation output: for 0 seconds — [0, 0], for 5 — [10, 0], for 10 — [20, 0]. The model intentionally does not account for discrete completions, spikes, errors, or changes in request duration. It shows the impact of process busy time. To forecast a real store, its own measurements are needed.

How to choose the next step based on observations

Observation at the moment of delay

Next step

There is a queue, no free processes, the pool has reached its limit

Analyze request duration and reasons for process occupancy. Evaluate the limit together with memory and the database.

There is a queue, the number of processes is below the upper limit

Check the pool mode and the appearance of new processes; do not treat this as proof of an insufficient limit.

The pool queue is not visible, yet the response remains slow.

Check the application execution, database, external calls, and other segments of the route.

Only the old maximum queue value is high.

Collect data from the current episode; the previous peak does not explain the current one.

A zero queue in a single snapshot does not rule out a short spike between measurements. The presence of a queue does not indicate what specifically delayed the busy processes. The useful result of the check is establishing a temporal link between waiting and slowdown, followed by identifying the cause of long-running requests.

Increasing the number of processes without calculation can shift the bottleneck to memory or the database. If diagnostics truly indicate a lack of resources, the next step is assessing VPS resources under load scenarios. That article covers the conditions for load testing and scaling criteria.

Provide the developer with the episode timestamp, route, pool snapshots, and associated database or external call delays. If the server and code are managed by different teams, determine in advance who will continue the diagnostics: this issue is covered in the article about administration boundaries and store support. This ensures the task concludes with a clear next action rather than a message stating 'CPU is free'.

Discussion 0

Share your experience and ask questions. Comments without links appear after editorial review.

No comments yet. Start the discussion.