PHP-FPM tuning on a small VPS should start with a memory budget, not with blindly increasing `pm.max_children`. The useful question is: how many concurrent PHP requests can the server handle while leaving enough RAM for the database, operating system and cache?
Measure one worker first
Observe PHP-FPM RSS under representative traffic. If one worker happens to use 90 MB, twenty workers could consume about 1.8 GB for PHP alone. That is an illustration, not a universal rule - measure your application.
A working 4 GB VPS memory budget
| Component | Why it needs memory |
|---|---|
| OS and services | baseline operation and headroom |
| Database | buffers, cache and connections |
| PHP-FPM | worker count × observed RSS |
| Cache / monitoring / panel | additional persistent overhead |
the WordPress optimization guide matters because worker tuning cannot repair a heavy plugin or bad query. the database server guide helps account for another major memory consumer.
dynamic or ondemand?
dynamic
A useful starting mode for changing web traffic when spare workers and limits are based on measurement.
ondemand
Potentially useful for low or irregular traffic where idle-worker memory should be minimized.
Find the real limit empirically
- measure peak concurrency;
- observe request duration;
- watch the queue and `max_children reached` events;
- check swap, database latency and CPU at the same time;
- change one parameter and measure again.
On Unmanaged VPS, this tuning is part of your stack ownership. If you prefer delegated OS and web-stack operations, compare Managed VPS and confirm where performance tuning support ends.
A simple formula that is only a starting estimate
You can think about the upper bound like this:
RAM available to PHP ÷ observed average RSS per worker = theoretical worker count.
Do not copy that result directly into `pm.max_children`. You still need headroom for traffic bursts, database growth, the OS page cache and unusually expensive requests. Production limits should normally sit below a purely theoretical maximum.
Which metric points to which problem?
| Signal | Possible interpretation |
|---|---|
| `max_children reached` | concurrency limit is low or requests are taking too long |
| queue grows while CPU is available | worker limit or blocking operation |
| swap grows | workers, DB and cache together exceed memory |
| CPU is saturated and queue grows | adding workers may make things worse |
| a few requests are extremely slow | slowlog/application profiling may matter more |
FPM status and slowlog belong in the tuning process
Tuning is not just watching `top`. FPM status metrics can expose active and idle workers, queues and process-manager saturation. Slowlog helps identify the PHP call path behind requests that exceed a chosen duration.
A 24-hour tuning cycle
- record a baseline during a real peak period;
- change one major parameter;
- observe the same traffic pattern;
- compare p95 response time, queue, CPU, RAM and errors;
- roll back if the result is worse.
The most common mistake is confusing a capacity problem with an application problem. If one request takes three seconds because of a bad query, more workers may simply run more slow requests at the same time.



