PHP-FPM pm.max_children on a 1 GB VPS: How to Calculate It and Verify It Under Load

On a 1 GB VPS running nginx and PHP-FPM, the single most consequential number in the entire configuration is pm.max_children. Set it too low and requests queue behind a busy worker while the CPU sits idle. Set it too high and the OOM killer resolves the ambiguity for you, usually at the worst possible moment. This is how to calculate it, what to change alongside it, and how to verify the result under real load.

Measure the Worker Before You Size the Pool

The calculation needs one measured input: how much memory a single PHP-FPM worker actually holds under your application. Guessing this is how people end up with 25 children on a 1 GB host.

  • Start the pool with a small number of children and put real traffic through it, or replay a few hundred requests with ab or wrk.
  • Read resident memory per worker: ps -o pid,rss,cmd -C php-fpm8.3 — RSS is in kilobytes.
  • Take the largest value across workers, not the average. The peak worker is the one that decides whether you OOM.

Typical values in 2026, for a WordPress install with a page cache enabled and 15–25 plugins:

StackPer-worker RSSNotes
Bare PHP script / small API25–40 MBNo framework, opcache on
Laravel or Symfony, small app45–70 MBDepends heavily on autoload size
WordPress, lean plugin set50–70 MBObject cache off
WordPress + WooCommerce80–140 MBPeak pages during checkout are higher
WordPress + heavy page builder120–200 MBElementor-class builders inflate every request

The pm.max_children Formula

The standard calculation is memory available to PHP divided by per-worker RSS:

  • Step 1. Take total RAM and subtract a fixed overhead: 250 MB on a 1 GB box, 350 MB on 2 GB. That covers the kernel, sshd, systemd, nginx and the Docker daemon if present.
  • Step 2. Subtract the database. A tuned MariaDB with a 128 MB buffer pool uses roughly 300 MB total; with 256 MB, roughly 450 MB.
  • Step 3. The remainder is PHP’s budget. Keep 10% of it free as emergency headroom.
  • Step 4. Divide by the measured peak worker RSS and round down.

On a 1 GB VPS with MariaDB: 1024 − 250 − 300 = 474 MB, minus 10% headroom = 427 MB. At 60 MB per worker that is 7 children. At 110 MB per worker (WooCommerce) that is 3. The difference between those two answers is not a preference; it is arithmetic, and it is the reason generic “set it to 10” advice breaks sites.

Why Not Simply Use pm = dynamic With High Max

Many guides recommend pm = dynamic with pm.max_children set to 25 and let the process manager sort it out. On a 1 GB host this is a slow-motion OOM. The process manager will happily spawn children up to the maximum when requests queue, and the kernel will kill whichever process it finds largest. On small machines, the pool should be small and fixed, with the web server, not PHP, absorbing the excess.

Setting1 GB VPS2 GB VPS4 GB VPS
pmstatic (or dynamic with narrow range)dynamicdynamic
pm.max_children4–78–1216–24
pm.start_serversn/a for static48
pm.min_spare_serversn/a36
pm.max_spare_serversn/a814
pm.max_requests300–500500500
pm.process_idle_timeout10s10s10s

pm.max_requests deserves a note: recycling workers after a few hundred requests limits the damage from memory leaks in plugins. It costs one request of latency per recycle. On 1 GB, where every megabyte counts, 300 is a reasonable ceiling; leaving it at 0 on a budget box means a leaky plugin eventually becomes an outage.

Each Worker Needs a vCPU to Run On

Children beyond the number of available cores do not add throughput; they just queue inside PHP instead of in nginx. On a 1 vCPU plan, two or three workers can saturate the core, which is why a 1 vCPU VPS with 7 children runs no faster than one with 3 — it just uses more memory while doing it. The useful ratio is roughly 1.5–2 workers per vCPU for PHP-heavy work, and more only when requests spend time waiting on I/O (external APIs, slow database queries).

That is the core argument for the 2 vCPU / 2 GB tier over 1 vCPU / 1 GB for any real site: it roughly doubles both the cores available to run workers and the memory available to hold them. The price difference is usually $2–$4 a month, which is less than an hour of the time it takes to chase down an OOM incident.

Nginx Settings That Protect the Pool

A small PHP pool needs the web server in front of it to fail politely. Three directives do most of the work:

  • worker_connections 1024; — plenty for a budget box; the real limit will be PHP, not nginx.
  • keepalive_timeout 15; — shorter than default reduces the number of idle connections held against a small pool.
  • Upstream with max_fails=2 fail_timeout=30s so a struggling backend is temporarily bypassed rather than piled onto.

Then set a page cache so most requests never reach PHP at all. For WordPress, a static page cache in nginx — or a plugin that writes full-page HTML — typically serves 80–95% of requests without touching a worker. That changes the sizing calculation more than any PHP tunable: with a 90% cache hit rate, the pool only needs to absorb the remaining 10%, and peak concurrency at 50,000 monthly views drops to a handful of simultaneous requests.

Verify Under Load, Not on an Idle Box

Configuration that looks correct on an idle server frequently falls over under concurrency. Test it:

  1. Run wrk -t2 -c10 -d60s --latency https://example.com/ against an uncached endpoint.
  2. In parallel, watch vmstat 2. Swap in/out should stay at zero; r (run queue) above your vCPU count means CPU-bound.
  3. Watch ps -o rss,cmd -C php-fpm8.3 | sort -n | tail -3 — confirm workers stayed within the sized footprint.
  4. Check the FPM status page (pm.status_path = /status) for listen queue and max children reached. A non-zero listen queue with spare RAM means max_children is too low; a max-children-reached count while swap grows means it is too high.
  5. Repeat after enabling the cache. Requests per second should rise sharply and CPU should fall.

If throughput plateaus while a listen queue forms and RAM is unused, add one or two children. If swap grows before throughput plateaus, remove them. The status page tells you which case you are in within a minute, and the numbers are far more reliable than a default config copied from a tutorial. Our RAM sizing figures by CMS and traffic level give you a second opinion on whether the plan itself is the problem, and the budget VPS comparison table shows what the next tier up costs when it is.

Affordable-Vps-Server-Author
Affordable-Vps-Server-Author
Articles: 354

Leave a Reply