Websites & apps

Tune PHP-FPM pools: pm.max_children and process managers

Calculate pm.max_children from your RAM, choose dynamic, static or ondemand, enable the status page and slow log, and fix 'server reached max_children' warnings.

AdvancedLinux VPS3 min read

PHP-FPM runs a pool of PHP worker processes behind Nginx or Apache. Too few workers and visitors queue up with 502/504 errors; too many and the server runs out of RAM. PHP-FPM tuning mostly comes down to sizing pm.max_children correctly for your memory.

Before you start

  • Root access to a VPS running PHP-FPM (any PHP 7.4–8.x version).
  • Find the pool file: /etc/php/8.3/fpm/pool.d/www.conf on Debian/Ubuntu, /etc/php-fpm.d/www.conf on AlmaLinux/Rocky.
  • Let the site run under normal traffic for a while so process sizes are realistic.

Measure memory per PHP process

ps --no-headers -o rss -C php-fpm8.3 | awk '{sum+=$1; n++} END {print sum/n/1024 " MB average"}'

Use php-fpm as the process name on RHEL-based systems. WordPress and WooCommerce workers often use 60–120 MB each; simple apps far less.

Calculate pm.max_children

Work out the RAM available to PHP: total RAM minus what MySQL, Redis, the web server and the OS need. Then:

pm.max_children = RAM for PHP / average process size

Example: 4 GB VPS, MySQL uses 1.2 GB, OS and Nginx 0.6 GB, leaving about 2.2 GB. With 80 MB per worker: 2200 / 80 ≈ 27. Round down and leave headroom: 24.

Choose a process manager

pm modeBehaviourGood for
dynamicKeeps between min and max spare workersMost sites
staticAlways runs max_children workersBusy dedicated servers with predictable load
ondemandStarts workers on request, stops idle onesMany low-traffic pools, small RAM

A balanced dynamic pool for the example above:

pm = dynamic
pm.max_children = 24
pm.start_servers = 6
pm.min_spare_servers = 4
pm.max_spare_servers = 10
pm.max_requests = 500

pm.max_requests recycles each worker after 500 requests, which contains slow memory leaks.

Enable the status page and slow log

In the pool file:

pm.status_path = /fpm-status
slowlog = /var/log/php-fpm-slow.log
request_slowlog_timeout = 5s

Expose the status page only to localhost in Nginx:

location = /fpm-status {
    allow 127.0.0.1;
    deny all;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}

Check it with curl http://127.0.0.1/fpm-status. Watch listen queue (should be 0) and max children reached. The slow log shows a stack trace for every request over 5 seconds, pointing at the plugin or query responsible.

Apply and verify

php-fpm8.3 -t && systemctl reload php8.3-fpm

Then watch memory with free -h under load. Available RAM should stay comfortably above zero and swap should not climb.

Troubleshooting

  • WARNING: [pool www] server reached pm.max_children setting: all workers were busy. Raise the limit only if RAM allows; otherwise make requests faster (caching, OPcache, slow log fixes).
  • 502 Bad Gateway with high load: workers are exhausted or crashed. Check the PHP-FPM log and listen.backlog.
  • Server swapping or OOM kills: max_children is too high for available RAM. Lower it.
  • seems busy (you may need to increase pm.start_servers): raise start_servers and min_spare_servers a little.

PHP-FPM FAQ

Does more max_children make my site faster?

Only up to the number of CPU cores and the RAM you have. Beyond that, requests compete for CPU and the server slows down or swaps.

Should I enable OPcache?

Yes. OPcache caches compiled PHP and is one of the biggest free speed-ups. It is enabled by default on most modern PHP builds.

Can I tune PHP-FPM on cPanel hosting?

On shared hosting the provider manages it. On MdmHost cPanel hosting, PHP runs through LiteSpeed's LSAPI, which manages workers automatically.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.