When a WordPress or WooCommerce site feels slow or starts throwing 502/504 errors under load, we often find the root cause in one place: misconfigured PHP-FPM pools. The three settings that matter most are pm, pm.max_children and pm.max_requests. They decide how many PHP workers you really have, how much RAM those workers will eat, and how gracefully they recycle over time. In this article, we will walk through how we at dchost.com choose these values in real projects, from small blogs to busy WooCommerce stores.
We will avoid vague advice like “set it to 50” and instead build a simple, repeatable way to size your PHP-FPM pools based on available RAM, process memory usage and expected concurrency. We will also show how tuning differs between a content site and a checkout-heavy WooCommerce store, and how to verify that your choices are actually working in production. By the end, you should be able to look at a server and say: “this is how many PHP workers it can safely run, and here is the config to match”.
İçindekiler
- 1 Why PHP-FPM Tuning Matters So Much for WordPress and WooCommerce
- 2 Quick Primer: How PHP-FPM Works
- 3 Understanding pm, pm.max_children and pm.max_requests
- 4 The Core Formula: How to Size pm.max_children Safely
- 4.1 Step 1: Measure Average PHP-FPM Process Memory
- 4.2 Step 2: Decide How Much RAM You Can Give to PHP-FPM
- 4.3 Step 3: Compute pm.max_children
- 4.4 Example 1: Small WordPress Blog on 2 GB VPS
- 4.5 Example 2: Mid-Size WooCommerce Store on 8 GB NVMe VPS
- 4.6 Example 3: High-Traffic WooCommerce on 16 GB Server
- 5 Choosing pm, pm.max_children and pm.max_requests for WordPress vs WooCommerce
- 6 Typical PHP-FPM Configs for Common Hosting Scenarios
- 7 Monitoring and Iterative Tuning: Did You Get It Right?
- 8 Putting It All Together: A Practical Checklist
- 9 Conclusion: A Calm, Data-Driven Way to Tune PHP-FPM
Why PHP-FPM Tuning Matters So Much for WordPress and WooCommerce
WordPress and WooCommerce are both PHP applications. Every uncached page view, AJAX request or admin action runs through PHP-FPM. If PHP-FPM is tuned too small, visitors queue for a free worker and your Time To First Byte (TTFB) rises. If it is tuned too large, workers fight for RAM, the server swaps, and everything becomes slow or unstable.
Unlike simple static hosting, a dynamic site has three critical bottlenecks: CPU, RAM and the database. PHP-FPM sits in the middle, brokering every dynamic request to PHP code and MySQL. That is why we always tune PHP-FPM side by side with MySQL, caching and core PHP ini settings like memory_limit and max_execution_time. For WooCommerce, which has heavier queries and more authenticated traffic, the impact is even stronger.
If you get PHP-FPM right, you usually see three immediate benefits:
- Lower median and p95 TTFB for uncached requests
- Far fewer 502/504 gateway errors during traffic spikes
- Much more predictable CPU and RAM usage under load
Let’s start with a quick reminder of how PHP-FPM works under the hood.
Quick Primer: How PHP-FPM Works
PHP-FPM (FastCGI Process Manager) is a daemon that keeps a pool of PHP worker processes ready to handle incoming requests from the web server (Nginx, Apache with proxy_fcgi, LiteSpeed, etc.). Each worker handles one request at a time. When the request is done, the worker becomes idle and waits for the next one.
Key points to keep in mind:
- Each worker is a PHP process that consumes RAM. The more plugins and themes you load, the bigger each process becomes.
- Your effective concurrency is roughly “number of workers” per pool. 10 workers means at most 10 concurrent uncached PHP requests.
- Workers can be pre-spawned (ready ahead of time) or spawned on demand, depending on the pm mode.
- Workers are occasionally recycled to avoid memory leaks and long-living processes. This is what pm.max_requests is about.
PHP-FPM settings live in pool config files, usually under /etc/php-fpm.d/ (RedHat/AlmaLinux/Rocky) or /etc/php/*/fpm/pool.d/ (Debian/Ubuntu). On many control panels, each site or account gets its own pool, which is ideal for isolation and per-site tuning. For a deeper, practical look at PHP-FPM itself, you can also read our article on server-side secrets that make WordPress fly with PHP-FPM and OPcache.
Understanding pm, pm.max_children and pm.max_requests
pm: Choosing the Process Manager Mode
The pm setting controls how PHP-FPM manages worker processes. You will typically see one of three values:
- pm = static: A fixed number of workers equal to
pm.max_childrenare always kept running. - pm = dynamic: PHP-FPM keeps a minimum number of workers and scales up to
pm.max_childrenas needed. - pm = ondemand: Workers are spawned when requests come in and killed after being idle for a while.
How to choose for WordPress and WooCommerce:
- dynamic – Our default for most WordPress and WooCommerce setups. It balances resource usage and responsiveness, especially under fluctuating load.
- static – Good for very stable, high-traffic environments where you know exactly how many workers you need and want predictability over elasticity.
- ondemand – Useful on very small VPS or low-traffic sites to save RAM when there is no traffic, but it can hurt TTFB on the first request after idle periods.
For typical WooCommerce stores on a VPS, we almost always start with pm = dynamic. On heavily optimized, high-traffic clusters, we sometimes use pm = static for tighter control.
pm.max_children: How Many PHP Workers You Really Have
pm.max_children is the hard cap on how many PHP worker processes can exist in a pool at once. This number effectively defines your maximum concurrent PHP requests for that pool.
If you set this number too low, requests line up waiting for a free worker, and you see increased response times and sometimes 502 errors from the web server (timeout while waiting for PHP). If you set it too high, workers consume more RAM than you actually have, the server starts swapping, and the entire box slows down or even becomes unstable.
Getting pm.max_children right is mostly about RAM math: how big each worker is, and how much memory you can dedicate to PHP-FPM after accounting for MySQL, cache (Redis/Memcached), the OS and other services.
pm.max_requests: Recycling Workers to Avoid Memory Leaks
pm.max_requests tells PHP-FPM how many requests a single worker will handle before it is gracefully restarted. After serving that many requests, PHP-FPM kills the worker and spawns a new one. This helps:
- Mitigate memory leaks in PHP, extensions or plugins
- Keep long-living processes from growing too large over time
- Refresh APCu/OPcache in some edge cases (though OPcache has its own policies)
If pm.max_requests is set too low, workers get recycled too often, adding overhead and occasionally causing short CPU spikes. If it is set too high or left unlimited, memory usage can creep up over days until you suddenly hit swap or OOM kills.
For WordPress/WooCommerce, we typically start with values between 300 and 1,000. Heavier stores with many plugins often benefit from a slightly lower value to keep leaks in check, while simple blogs can afford a higher one.
The Core Formula: How to Size pm.max_children Safely
Let’s build a simple formula you can reuse on any server. The basic idea is:
maximum PHP workers ≈ available RAM for PHP / average RAM per PHP process
We will refine this with safety margins, but that is the foundation.
Step 1: Measure Average PHP-FPM Process Memory
You cannot guess this accurately; plugins and themes change everything. Measure it on your own site under realistic load. On a Linux VPS or dedicated server, run (adjust PHP version/pool name as needed):
ps -o rss,cmd -C php-fpm | grep 'pool=www'
rss is resident set size in KB. You can also use top or htop and look at PHP-FPM processes during a period of active traffic: browse the site, run a few WooCommerce checkouts, open wp-admin, etc.
Example: you observe several PHP-FPM workers using between 120 MB and 180 MB RSS. You can take 160 MB as a conservative average. In KB that is about 160,000, but for easier math we will keep values in MB.
Step 2: Decide How Much RAM You Can Give to PHP-FPM
Your PHP-FPM pool does not own the whole server. On a box running WordPress/WooCommerce you typically have:
- The OS and background services (SSH, systemd, etc.)
- Web server (Nginx/Apache/LiteSpeed)
- Database (MySQL/MariaDB/PostgreSQL)
- Cache layer (Redis/Memcached), maybe Elasticsearch or OpenSearch
- Monitoring agents, backup agents, etc.
On a dedicated WordPress/WooCommerce server or NVMe VPS at dchost.com, a reasonable first approximation is:
- Reserve 30–40% of total RAM for MySQL and caches
- Reserve 10–20% for OS and web server
- Give the remaining 40–60% to PHP-FPM
On a small 4 GB VPS, for example, we might budget:
- 1.2 GB for MySQL + Redis
- 0.8 GB for OS + Nginx
- 2.0 GB for PHP-FPM
If you run multiple PHP pools (for multiple sites), that PHP budget must be split across them. Agencies hosting many WordPress sites on one stack often find our guide on hosting architecture for agencies managing 20+ WordPress sites useful here.
Step 3: Compute pm.max_children
Now we divide the PHP-FPM RAM budget by the average process size and add a safety margin:
pm.max_children = floor( PHP_RAM_budget_MB / avg_process_MB ) × safety_factor
We usually use a safety_factor between 0.7 and 0.8 to avoid edging too close to real limits (there are always spikes).
Example 1: Small WordPress Blog on 2 GB VPS
Suppose:
- Total RAM: 2 GB
- Average PHP worker RSS: 80 MB
- We allocate 900 MB to PHP-FPM
Math:
- Raw max workers: 900 / 80 ≈ 11.25 → 11
- With 0.8 safety factor: 11 × 0.8 ≈ 8.8 → 8 workers
So we would set:
pm = dynamic
pm.max_children = 8
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 500
For a low-traffic blog with good full-page caching, 8 workers is usually more than enough.
Example 2: Mid-Size WooCommerce Store on 8 GB NVMe VPS
Now consider a WooCommerce store using many plugins and a heavier theme:
- Total RAM: 8 GB
- Measured PHP worker RSS: 150–220 MB → take 200 MB for safety
- We allocate 3.5 GB to PHP-FPM
Math:
- Raw max workers: 3,500 / 200 = 17.5 → 17
- Safety factor 0.75: 17 × 0.75 ≈ 12.75 → 12 workers
Config could look like:
pm = dynamic
pm.max_children = 12
pm.start_servers = 4
pm.min_spare_servers = 4
pm.max_spare_servers = 8
pm.max_requests = 400
This gives you up to 12 concurrent PHP requests. Combined with aggressive full-page caching for anonymous traffic and object caching for logged-in users, this often comfortably handles a few hundred concurrent visitors with occasional checkout bursts. For capacity planning specifically for WooCommerce, we go into more detail in our guide on WooCommerce capacity planning and sizing vCPU/RAM/IOPS.
Example 3: High-Traffic WooCommerce on 16 GB Server
On a busy store, maybe behind a CDN with well-tuned cache rules, you might see:
- Total RAM: 16 GB
- Average PHP worker RSS: 250 MB (large plugin stack, heavy theme)
- PHP-FPM budget: 7 GB
Math:
- Raw workers: 7,000 / 250 = 28
- Safety 0.75: 28 × 0.75 = 21 → pm.max_children = 21
Config:
pm = static
pm.max_children = 21
pm.max_requests = 500
Here we might choose pm = static because the store has very predictable high load and we want all 21 workers pre-warmed and ready. This is the sort of configuration we deploy on dedicated WooCommerce servers or higher-end VPS at dchost.com where we know the resource envelope very well.
Choosing pm, pm.max_children and pm.max_requests for WordPress vs WooCommerce
WordPress Blogs and Content Sites
A typical content site has:
- High ratio of anonymous traffic
- Effective full-page caching (Nginx microcaching, LiteSpeed Cache, or a CDN)
- Less complex plugins compared to an e-commerce stack
This means most requests do not hit PHP at all if caching is well configured. For those that do, they are often relatively light. On these sites we often see smaller PHP workers (60–120 MB) and can run more workers per GB of RAM.
For a medium blog on a 4 GB VPS with good caching, a reasonable starting point might be:
pm = dynamic
pm.max_children = 12
pm.start_servers = 3
pm.min_spare_servers = 3
pm.max_spare_servers = 6
pm.max_requests = 800
We also strongly recommend pairing this with good full-page caching. Our article on server-side optimization for WordPress with PHP-FPM, OPcache, Redis and MySQL shows how much relief caching can give your PHP workers.
WooCommerce Stores
WooCommerce changes the picture in a few important ways:
- More authenticated traffic (logged-in customers and admins)
- Cart, checkout, My Account and admin are mostly uncacheable as full pages
- More plugins: payment gateways, shipping, marketing, reporting, etc.
- More AJAX (cart fragments, live search, shipping calculators)
All of this increases average request cost and inflates PHP worker memory usage. It also means that even with aggressive HTML caching for product/category pages, you must size PHP-FPM for checkout peaks.
For a small WooCommerce store on a 4 GB VPS, we might start with something like:
pm = dynamic
pm.max_children = 8
pm.start_servers = 3
pm.min_spare_servers = 3
pm.max_spare_servers = 6
pm.max_requests = 300
On a bigger 8–16 GB server hosting a single store, 12–24 workers is common, as in the earlier examples. Just remember that increasing pm.max_children increases RAM usage nearly linearly, so you must re-check memory consumption after changes.
pm.max_requests for WordPress and WooCommerce
We almost never leave pm.max_requests at the default of 0 (unlimited). Over days or weeks, you commonly see PHP workers grow in RSS as they handle more complex requests. A moderate recycle limit keeps things in check without causing churn.
Typical ranges we use in real projects:
- Small WordPress site: pm.max_requests = 800–1,000
- Medium content or light WooCommerce: 500–800
- Heavy WooCommerce with many plugins: 300–500
If you notice that PHP-FPM memory usage keeps creeping up over time (e.g., every day RSS grows by a few hundred MB), try lowering pm.max_requests a bit and monitor again.
Typical PHP-FPM Configs for Common Hosting Scenarios
On shared hosting, you often do not have direct control over pm settings; the provider sets global limits for all accounts. In that situation, your main levers are:
- Optimizing
memory_limitand other PHP ini values (see our detailed guide on choosing PHP memory_limit, max_execution_time and upload_max_filesize) - Using full-page caching (LiteSpeed Cache, Nginx-based cache, or a CDN)
- Reducing heavy plugins and simplifying the theme
If you constantly hit resource limits on shared hosting, it might be time to move to a VPS where you can control PHP-FPM directly. We have a step-by-step checklist for a smooth transition in our article on moving from shared hosting to a VPS without downtime.
2) Single WordPress Site on a Small VPS (2–4 GB)
For a small business site or blog with moderate traffic on a 2–4 GB VPS, a good starting point is:
- Measure PHP worker RSS (typically 70–120 MB)
- Allocate ~40–50% of RAM to PHP-FPM
- Use pm = dynamic with 6–12 workers
Example for 4 GB RAM, 100 MB workers, 1.5 GB to PHP:
pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 3
pm.max_spare_servers = 6
pm.max_requests = 800
Pair this with OPcache enabled (and sized reasonably), a caching plugin, and database tuning and you have a very capable small stack.
3) Single WooCommerce Store on 8–16 GB VPS
For a store that expects real-time traffic, carts and checkouts, we increase both the worker memory estimate and concurrency:
- Measure PHP worker RSS (often 150–300 MB)
- Allocate 40–60% of RAM to PHP-FPM
- Target 12–24 workers, depending on RAM and concurrency needs
Example for 8 GB RAM, 200 MB workers, 3.5 GB to PHP (as before):
pm = dynamic
pm.max_children = 12
pm.start_servers = 4
pm.min_spare_servers = 4
pm.max_spare_servers = 8
pm.max_requests = 400
If your store is growing fast, plan ahead not only for PHP-FPM but also for MySQL, Redis and potential future separation of roles. Our article on when WooCommerce really needs separate database and cache servers explains when this step becomes logical.
4) Multi-Site / Multi-Tenant WordPress on One Server
If you host several sites or a WordPress Multisite network on one VPS or dedicated server, you will typically have:
- One pool per site (stronger isolation, clearer metrics)
- Or one pool per cluster of similar sites (simpler config, less overhead)
The formula is the same, but you now split the PHP RAM budget across pools. High-traffic or heavy WooCommerce pools get more workers; small brochure sites get fewer. On dchost.com multi-site stacks, we often allocate a base of 2–4 workers for tiny sites and 6–16 for primary sites.
Monitoring and Iterative Tuning: Did You Get It Right?
PHP-FPM tuning is not a “set once and forget forever” task. Traffic, plugins and code change over time. You should validate your configuration and revisit it regularly, especially after major theme or plugin changes.
Key Things to Monitor
- PHP-FPM status page (if enabled): see active processes, idle processes, max active, queue length.
- Server RAM: watch for swap usage; if swap grows steadily, you are likely overcommitted.
- CPU usage: consistently maxed-out CPU suggests either too many workers or very heavy code.
- Web server logs: 502/504 errors, upstream timeout errors, long request durations.
- Access logs: burst patterns around campaigns, newsletters, or sales.
Even simple tools like top, htop, vmstat and journalctl -u php-fpm tell you a lot. For a deeper view on diagnosing slow responses, including TTFB issues caused by PHP-FPM, check out our guide on fixing high TTFB on WordPress and PHP sites.
Symptoms and How to Adjust
Symptom 1: High CPU, but low RAM, frequent 502/504 under spike
- Possible cause: too many workers, CPU saturated.
- Action: lower
pm.max_childrenslightly; optimize slow queries and heavy plugins; ensure caching is doing its job.
Symptom 2: RAM constantly near 100%, swap used, system feels sluggish
- Possible cause: too many workers or oversized
memory_limit. - Action: reduce
pm.max_children, revisitmemory_limit, reduce plugin bloat; consider upgrading to a plan with more RAM.
Symptom 3: Requests sometimes wait a long time before PHP starts processing
- Possible cause: too few workers; PHP-FPM queue building up.
- Action: increase
pm.max_childrenslightly if RAM allows; verify that caching is correct so you are not wasting workers on anonymous traffic.
Symptom 4: PHP-FPM memory usage grows day by day, then resets after reloads
- Possible cause: memory leaks, too high or unlimited
pm.max_requests. - Action: reduce
pm.max_requestsin steps (e.g., 1,000 → 600 → 400), monitor again.
Putting It All Together: A Practical Checklist
To recap the process we follow for WordPress and WooCommerce sites on dchost.com infrastructure, here is a step-by-step checklist you can adapt:
- Enable OPcache and caching (page cache + object cache) so PHP-FPM is not doing unnecessary work.
- Measure average PHP worker RSS under realistic load using
ps,toporhtop. - Define a RAM budget for PHP-FPM after reserving memory for MySQL, caches, OS and other services.
- Compute pm.max_children using the formula and add a safety factor of 0.7–0.8.
- Pick pm mode: usually
pm = dynamicfor most sites;staticfor high-traffic, well-understood stores. - Set pm.max_requests in a moderate range (300–1,000) depending on complexity.
- Reload PHP-FPM, monitor RAM/CPU and access logs for a few days.
- Iterate: adjust
pm.max_childrenandpm.max_requestsbased on real data, not guesswork.
As your traffic grows, plan capacity upgrades early instead of waiting for pain. That might mean scaling up to a larger VPS, moving to a dedicated server, or separating database and cache roles. Our articles on WooCommerce capacity planning and when to split database and cache servers can help you design that roadmap.
Conclusion: A Calm, Data-Driven Way to Tune PHP-FPM
Tuning PHP-FPM for WordPress and WooCommerce does not have to be mysterious. Once you understand what pm, pm.max_children and pm.max_requests actually do, the problem turns into straightforward capacity planning: measure how big each worker is, decide how much RAM you can safely spend, and let the math tell you how many workers you can run.
From our experience managing WordPress and WooCommerce workloads on dchost.com servers, the biggest wins come from combining smart PHP-FPM settings with good caching, realistic PHP ini limits and a clean plugin stack. If you are constantly fighting resource limits on shared hosting, or if your WooCommerce checkout slows down during campaigns, it is probably time to look at both your PHP-FPM config and the underlying hosting resources.
If you would like a hosting environment where you control PHP-FPM pools fully and have room to grow, our NVMe VPS, dedicated server and colocation options are designed with exactly these kinds of workloads in mind. Size your server realistically, apply the formulas from this guide, and keep an eye on real-world metrics. With that combination, PHP-FPM stops being a bottleneck and becomes a solid foundation your WordPress or WooCommerce site can grow on.
