Bali, Indonesia
Insights

Choosing PHP-FPM pm Mode: static, dynamic or ondemand on a Small VPS

October 22, 2026
0:00
Most small VPS setups run PHP-FPM with whatever process manager mode came in the package or the control panel's template, and nobody revisits it until the server starts swapping or the first page load after a quiet hour feels slow. The pm setting decides how many PHP worker processes exist and when they get created. On a VPS with 1 or 2 GB of RAM that choice matters, because PHP workers are usually the largest memory consumer after the database. There are three modes. static keeps a fixed number of workers alive all the time. dynamic keeps a pool that grows and shrinks between a minimum and a maximum. ondemand starts with no workers and forks them only when requests arrive, then kills them after they sit idle. None of them is the best mode. The right one depends on a few questions about your server, and they are easier to answer in a fixed order.
  1. Work out the RAM budget for PHP before touching the mode. Take total RAM and subtract what the OS, the web server, the database, and any cache like Redis need under normal load, then leave some headroom. The remainder is what PHP workers can use. If you skip this step, every mode can push the box into swap. The mode only changes when memory gets used, not how much the worst case uses.
  2. Measure how big one worker really is on your site. Worker size depends on the application, the plugins, and memory_limit, so a number from a blog post will not match your server. Let the site take some normal traffic, then check the resident memory of the pool workers:
BASH
# Read-only: count PHP-FPM pool workers and show average RSS in MB
ps -eo rss,cmd | grep '[p]hp-fpm: pool' | awk '{s+=$1; n++} END {if (n) print n, s/n/1024}'
The first number is the worker count and the second is the average size in MB. Process titles can differ between distros and panels, so if nothing matches, run ps aux | grep php and adjust the pattern. RSS counts shared memory such as OPcache in every worker, so this figure overstates real usage a bit. For sizing, that conservative error is the safe direction.
  1. Set pm.max_children from those two numbers, whatever mode you pick. As an illustrative example, if you can give PHP 800 MB and a worker averages 80 MB, a ceiling of about 10 workers is reasonable. pm.max_children is the hard cap in all three modes, and it is the setting that actually protects you from running out of memory. If you set it too high, a traffic spike or a crawler can start more workers than RAM can hold. If you set it too low, requests queue up and visitors see slow pages or gateway errors. Get this number right first, then choose the mode.
  1. Steady, predictable traffic and memory to spare: consider static. When a site is busy most of the day and the server is dedicated to it, static removes all spawning overhead. Workers are always warm and ready. The trade-off is that pm.max_children workers stay resident even at 4 a.m., so static only makes sense when your RAM budget already assumes all of them are running. On a small VPS hosting several things at once, that is rarely the case.
  2. Regular traffic with peaks and quiet periods: dynamic is the usual middle ground. Dynamic keeps a few idle workers ready, adds more during busy moments, and trims back down afterwards. This fits a typical business site or a small store, where traffic follows working hours with occasional bursts. It is also the default in many distro packages, which is why so many servers already run it. Whether the default spare counts suit a small box is a separate question, covered below.
  3. Many low-traffic sites, or long idle stretches: lean toward ondemand. Every pool in PHP-FPM manages its own workers. If you host ten small sites as ten pools in dynamic mode, each one keeps its own idle spares, and those idle processes add up to a large share of RAM doing nothing. Ondemand lets idle pools drop to zero workers, so memory only goes to sites that are actually getting requests. This is a common pattern on multi-site servers, and it is one of the first things a support engineer looks at when a small VPS runs out of memory without any real traffic.
  4. Decide how much cold start you can tolerate. The cost of ondemand is that the first request after an idle period has to wait for a worker to be forked. OPcache lives in shared memory owned by the master process, so a new worker does not recompile your code from scratch. That keeps the delay small, but it is not zero, and under a sudden burst many forks can happen at once. For an admin panel or a brochure site, nobody will notice. For a checkout page or an API that must answer quickly after quiet periods, dynamic with one or two spares is usually the more comfortable choice.
  1. If you choose dynamic, size the spare settings for a small box. Dynamic uses pm.start_servers, pm.min_spare_servers, and pm.max_spare_servers alongside pm.max_children. Spare workers are idle processes held in reserve, and on a 1 GB VPS a high spare count is just memory sitting unused. Rules of thumb: keep pm.min_spare_servers low (one or two is often enough for a small site), keep pm.max_spare_servers well under pm.max_children, and set pm.start_servers somewhere between the two. PHP-FPM checks that these values are consistent and refuses to start if they are not, so read the error carefully after a reload.
  2. If you choose ondemand, look at pm.process_idle_timeout. This controls how long an idle worker lives before it is killed. Upstream defaults to 10 seconds, which is aggressive. On a site with traffic every minute or so, workers end up dying and respawning constantly. Raising the timeout to something like a minute or a few minutes (illustrative values) keeps workers warm between nearby requests while still freeing memory overnight. It is a dial between memory saving and cold starts, so set it based on how bursty your traffic is.
  3. Consider pm.max_requests if worker memory creeps up over time. Some applications and plugins leak memory slowly, so a worker that started at a reasonable size gets bigger as it serves more requests. pm.max_requests recycles a worker after a set number of requests. The default is 0, meaning workers are never recycled. A moderate value adds a little respawn overhead in exchange for keeping worker size close to what you measured in step 2. This matters most in static mode, where workers otherwise live as long as the service does.
  1. Check the PHP-FPM log for capacity warnings. If the log shows server reached pm.max_children setting, requests were waiting for a free worker during that period. In dynamic mode, a warning that the pool seems busy and suggesting you raise pm.start_servers or the spare settings means spawning could not keep up with bursts. Neither is automatically a reason to raise limits. First check whether the RAM budget from step 1 allows it, and whether slow queries or external API calls are holding workers busy longer than they should.
  2. Use the status page if you can expose it safely. Setting pm.status_path in the pool and routing it through the web server gives you live numbers: active and idle processes, the listen queue (requests waiting for a worker), and a max children reached counter. A listen queue that stays above zero means too few workers. Lots of idle processes with plenty of free RAM in ondemand mode suggest you could afford dynamic. Restrict that URL to localhost or your own IP, because it reveals internal details about the server.
  3. Watch memory and swap, not just page speed. After changing the mode, compare memory use and swap activity over a normal day and a quiet night. A good choice shows memory falling during quiet periods (dynamic or ondemand) or staying flat at a level you planned for (static), with no swapping during peaks. Reload PHP-FPM after each change rather than restarting the whole server. The service name depends on your distro or panel, so confirm it before running anything.
If you only have time for one change, set pm.max_children from measured worker size and your real RAM budget, because that cap prevents more trouble than any mode switch. Then pick the mode from the shape of your traffic: static for a busy dedicated site with memory to spare, dynamic with small spare counts for a typical single site, and ondemand with a sensible idle timeout when several quiet sites share one small VPS.