Bali, Indonesia
Insights

Reading PHP-FPM Logs to Confirm max_children Is Causing Your 502 Errors

October 21, 2026
0:00
A busy WordPress site throws 502 Bad Gateway errors a few times a day, then recovers on its own. The usual first reaction is to raise pm.max_children and hope it goes away. Sometimes that is the right fix. Often it only hides a slow database, a stuck external API call, or a PHP worker that keeps crashing. Before you change any numbers, use the logs to confirm two things: PHP-FPM actually ran out of workers, and the web server's errors happened at the same moments customers saw the 502. This checklist covers which lines to look for, in what order, and how to line up their timestamps with what was reported.
PHP-FPM (FastCGI Process Manager) runs a pool of PHP worker processes. pm.max_children caps how many can exist at once. When every worker is busy, new requests do not fail right away. They wait in the socket's listen queue until a worker frees up. The web server returns an error only when that waiting breaks down. One way is a full queue, so the connection gets refused, which usually shows up as a 502. The other is a wait longer than the web server's read timeout, which usually shows up as a 504. So a max_children problem leaves a pattern rather than a single line: FPM reports it hit the limit, and the web server logs connection or timeout errors toward the FPM socket in the same window. If you want the bigger picture on how pools get starved under heavy traffic and how to restructure them, Unraveling PHP-FPM Pool Starvation in High-Concurrency WordPress Hosting goes deeper. This article stays focused on proof.
  1. Find the real FPM log first. The location comes from the error_log directive in the global FPM config, not the pool file, and it differs per distro, PHP version, and control panel. Open the config and read the path instead of guessing. Otherwise you can end up searching an empty file and wrongly conclude FPM is fine.
  2. Search for the max_children warning. The line you want reads like WARNING: [pool www] server reached pm.max_children setting (N), consider raising it. The pool name in brackets matters on servers with one pool per site or per account, because it tells you which site saturated. FPM usually writes this once per saturation episode rather than once per waiting request. A single line can therefore stand for minutes of queued traffic, so don't judge severity by how many lines you see.
  3. Note the related warnings around it. With pm = dynamic you may also see a line that starts with seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers). That means FPM was spawning workers quickly, which often comes right before hitting the cap. Lines like child N exited on signal or execution timed out point to a different story: workers being killed or crashing. That can produce 502s even when the pool never filled.
  4. Check the status page if one is enabled. When pm.status_path is configured, the status output includes max children reached, listen queue, and max listen queue. A rising max children reached counter and a non-zero max listen queue back up the log warning. Both counters reset when FPM restarts, so read them before anyone reloads the service.
  1. Read the web server error log, not just the access log. The access log tells you a 502 happened. The error log tells you why. In Nginx, look at upstream errors pointing at your FPM socket or port:
    • connect() to unix:... failed (11: Resource temporarily unavailable) usually means the socket's listen queue was full, which fits pool saturation.
    • upstream timed out (110: Connection timed out) while reading response header from upstream means the request waited too long. Expect a 504 here, not a 502.
    • connect() failed (111: Connection refused) or No such file or directory on the socket means FPM was down or the socket path was wrong. That is a different problem.
    • recv() failed (104: Connection reset by peer) or upstream prematurely closed connection usually means a worker died mid-request. Look for crash or kill lines in the FPM log at the same time.
  2. On Apache with mod_proxy_fcgi, look for the proxy errors instead. Lines such as AH01067: Failed to read FastCGI header or AH01075: Error dispatching request to appear when the backend cannot answer. They are less specific than the Nginx messages, so the FPM-side warning carries more weight in your conclusion. On LiteSpeed, PHP often runs through lsphp rather than FPM, so the FPM log lines may not exist at all. Check what is actually serving PHP before spending time here.
  3. Confirm the error points at the right upstream. Shared servers and multi-site VPSs often have several pools. An Nginx error that names one socket and an FPM warning that names a different pool are not the same incident, even if the times match.
For a step-by-step walk through these Nginx and FPM log lines in Indonesian, see Membaca Log Nginx dan PHP-FPM untuk Melacak Sumber Error 502.
This is where most investigations go wrong. The log lines exist, but nobody checks that they belong to the same minute as the complaint.
  1. Normalize timezones before comparing. The FPM log uses a format like [20-Oct-2026 14:05:12], while the Nginx error log uses 2026/10/20 14:05:12. Depending on configuration, they may not use the same timezone. Customer reports, uptime monitors, and CDN dashboards often show UTC or the user's local time. Write down the offset for each source before you line anything up. A mismatched hour can make an unrelated warning look like the cause.
  2. Search in a narrow window around each report. Start with a few minutes on either side of the reported time, then widen if needed. Remember that logs rotate, so the incident may sit in a compressed older file. These are read-only searches. The paths are examples, so swap in the ones from your own config:
BASH
# PHP-FPM side: max_children warnings, including rotated .gz logs
zgrep -h 'max_children' /var/log/php-fpm/error.log*

# Nginx side: upstream errors during one example hour
zgrep -h 'upstream' /var/log/nginx/error.log* | grep '2026/10/20 14:'

# Access log: 502 and 504 responses in the same example hour
zgrep -h ' 50[24] ' /var/log/nginx/access.log* | grep '20/Oct/2026:14:'
  1. Check the order of events, not just that they overlap. The FPM warning should appear at or just before the first burst of upstream errors, and the errors should taper off after traffic drops. If the 502s start well before any FPM warning, or keep going long after FPM recovered, the pool limit probably isn't the main cause.
  2. Compare with the traffic at that time. Look at the request count in the access log for the same window. A spike in requests, a crawler, or a burst of wp-login.php or xmlrpc.php hits explains a lot. A full pool with normal traffic is a different conclusion: each request is taking longer than it used to.
Confirming the limit was hit does not mean raising it is the answer. If traffic was normal and workers still filled up, something was holding each worker longer. Common causes are slow queries, a remote HTTP call with no timeout, or heavy cron jobs running during the day. Enabling the FPM slowlog (request_slowlog_timeout plus slowlog) shows stack traces of the requests that stuck, which usually names the plugin or function. Raising pm.max_children without checking memory has its own risk. Each worker uses RAM, and too many can push the server into swap or trigger the OOM killer, which turns occasional 502s into a full outage. A common rule of thumb is to base the limit on available memory divided by typical worker size, measured on your own server rather than copied from a guide. In support, it helps to say this plainly to the site owner. 'The PHP pool filled up at these times' is a finding. 'Increase max_children' is only one possible fix, and the slowlog often shows a cheaper one. In practice: find the FPM server reached pm.max_children line, find the matching upstream errors in the web server log for the same pool, line both up with the reported times after fixing timezones, and only then decide between more workers and faster requests. If any one of those three doesn't line up, keep looking before you change the config.