sfw/fix
server reached pm.max_children high

PHP-FPM: server reached pm.max_children setting, consider raising it

The FPM pool ran out of worker processes, so new requests queued or died, showing up as slow pages or 502/504 errors.

What you see

[pool www] server reached pm.max_children setting (10), consider raising it

What’s actually happening

Site crawls or throws 502 Bad Gateway / 504 Gateway Timeout under load, then recovers when traffic drops. The PHP-FPM log fills with the "reached pm.max_children" warning, often several times a minute during the spike. Each entry means a request arrived and there was no free worker to hand it to — it either waited in nginx's queue or got dropped. Single-page-app backends and busy WooCommerce stores hit this first.

Common causes

  • pm.max_children is simply set too low for the concurrency the site now gets (default templates ship with 5–10).
  • Slow requests — external API calls, a blocking cron, an unindexed DB query — hold workers for seconds each, so even moderate traffic exhausts the pool.
  • A traffic spike or bot/crawler hitting uncached, PHP-heavy URLs all at once.
  • No page cache in front of PHP, so every hit (including logged-out homepage views) consumes a worker.
  • Memory ceiling reached: each child uses 40–120MB, so on a small box you can't raise max_children without swapping.

How to fix it

  1. Size pm.max_children to actual RAM, don't just bump it blindlyFind average worker memory: ps --no-headers -o rss -C php-fpm8.2 | awk '{s+=$1} END {print s/NR/1024 " MB"}'. Then max_children ≈ (RAM available for PHP) / (avg worker MB). On a 4GB box reserving 1GB for nginx/MySQL, ~50MB workers → about 60. Set it in the pool file (/etc/php/8.2/fpm/pool.d/www.conf), reload with systemctl reload php8.2-fpm. Overshooting just trades 502s for OOM-kills.
  2. Switch the pool to pm = dynamic and tune the sparesWith pm = dynamic, set pm.start_servers, pm.min_spare_servers, and pm.max_spare_servers so the pool keeps warm workers ready for bursts instead of spawning under load. A common shape: start=10, min_spare=5, max_spare=20, max_children=60. For very spiky traffic on tight RAM, pm = ondemand spawns only as needed.
  3. Find and kill the slow requests holding workersSet request_slowlog_timeout = 5s and slowlog = /var/log/php-fpm/www-slow.log in the pool. The backtraces show exactly what's blocking — usually a curl to a third-party API or a missing DB index. Fix those and each worker frees up faster, which raises effective capacity without adding a single process.
  4. Put a cache in front of PHPThe cheapest worker is the one you never call. Add FastCGI cache (nginx) or a full-page cache (Varnish, WP Rocket, LiteSpeed cache) so logged-out traffic never reaches FPM. This alone often turns a pool that maxed out at 60 concurrent into one that idles at 3.
  5. Confirm nginx isn't masking the real errorIf you see 502/504 but no FPM warning, the bottleneck may be upstream timeouts (fastcgi_read_timeout) or the listen.backlog filling. Check both logs together — FPM "max_children" plus nginx "upstream timed out" tells a different story than FPM warnings alone.

Stop it recurring

Set the slowlog and a RAM-based max_children from day one, and load-test before a campaign rather than discovering the ceiling in production.

Related errors