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.
Start with the memory you can actually give PHP
- 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.
-
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}'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.
- Set
pm.max_childrenfrom 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_childrenis 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.
Match the mode to how your traffic arrives
-
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 thatpm.max_childrenworkers 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. -
Regular traffic with peaks and quiet periods:
dynamicis 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. -
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. - 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.
Tune the knobs that come with your choice
-
If you choose
dynamic, size the spare settings for a small box. Dynamic usespm.start_servers,pm.min_spare_servers, andpm.max_spare_serversalongsidepm.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: keeppm.min_spare_serverslow (one or two is often enough for a small site), keeppm.max_spare_serverswell underpm.max_children, and setpm.start_serverssomewhere 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. -
If you choose
ondemand, look atpm.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. -
Consider
pm.max_requestsif 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_requestsrecycles 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.
Confirm the choice after a few days of real traffic
-
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 poolseems busyand suggesting you raisepm.start_serversor 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. -
Use the status page if you can expose it safely. Setting
pm.status_pathin 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. - 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.
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.