Why your PHP-FPM throws 502 under load (pm.max_children, explained)
Your PHP site runs fine, then traffic spikes and nginx starts throwing 502 Bad Gateway . Logs blame the upstream. The cause is almost always one number in your PHP-FPM pool: pm.max_children . What actually happens Nginx hands PHP requests to PHP-FPM. FPM has a fixed ceiling of worker processes — pm.max_children . When every worker is busy and the listen backlog fills, FPM can't accept the…
A PHP site functions well until a surge in traffic triggers Nginx to display 502 Bad Gateway errors. These logs point to an issue with the upstream. The root cause is usually the value of pm.max_children in the PHP-FPM pool.
Nginx sends PHP requests to PHP-FPM, which has a set limit of worker processes defined by pm.max_children. When all workers are engaged and the listen backlog is full, PHP-FPM cannot accept further connections, leading to the failure of nginx's fastcgi_pass and resulting in the 502 error. In the pm = dynamic configuration, this problem is highlighted in the FPM status page with a message indicating that the maximum number of children has been reached and the listen queue length. This maximum reached counter is a clear indicator that the ceiling has been hit.
To properly size the pm.max_children, a proper formula should be used: max_children = (RAM budget for FPM) / (average worker RSS). The average worker's resident memory (RSS) should be measured under the actual application, not the memory_limit (which is a cap, not the usage). One should decide on a RAM budget by subtracting the total RAM needed for the database, cache, and OS from the total RAM.
A buffer should be left. Dividing this budget by the average worker RSS gives the ceiling value. This value should be set as pm.max_children, not an arbitrary number from a blog post.
A particularly problematic scenario arises when multiple sites share a single FPM master and a single max_children value. If one site experiences a traffic spike, it consumes the maximum number of workers, causing the others to fail with 502 errors, even if they are idle. The solution is to assign each site its own PHP-FPM pool with a ceiling value calculated from its own resident memory.
Additionally, setting pm.max_requests can help recycle workers and prevent memory leaks. Monitoring slow requests in the status page and slowlog is also important as a few slow endpoints can overwhelm workers and create a false capacity problem.
To simplify this process, a read-only command-line tool named fpm-doctor can be used. It reads the PHP-FPM pool configurations and live status, measures the actual worker RSS, and recommends the optimal max_children based on the actual memory budget. The tool also flags the shared-pool pattern and ranks slowlog issues. It is written in Composer and as a .phar file, avoiding any modifications to the PHP-FPM configuration files.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.