A PHP worker is a single PHP-FPM child process that runs WordPress code for one HTTP request at a time. A pool of those workers serves your whole site. "Workers exhausted" means every worker in the pool is currently busy and the next request has to sit in a queue until one frees up. Once that queue grows or stalls, the visitor sees a slow page, a 502, or a 504.
What a PHP worker actually is
WordPress is PHP. Every uncached request to a WordPress site has to be turned into HTML by a PHP process. On modern hosting, that process is a child of PHP-FPM (FastCGI Process Manager), the long-running PHP daemon that the web server hands requests to over a FastCGI socket. Each child process is a worker. nginx (or Apache) accepts the HTTP request, then forwards it via fastcgi_pass to PHP-FPM, which assigns it to one free worker. That worker boots WordPress, runs the queries, builds the HTML, returns it, and goes back to the pool to wait for the next request.
Workers are organized in pools. A pool is defined by an FPM configuration file (typically /etc/php/8.3/fpm/pool.d/www.conf on Debian-family systems) and has a fixed upper bound on how many child processes it may run at once. That ceiling is pm.max_children. When all pm.max_children workers are simultaneously occupied, the pool is exhausted. There is no spillover and no automatic scale-up beyond the ceiling.
How a PHP-FPM pool actually scales
PHP-FPM has three process manager modes, and each one schedules workers differently. The mode is set with the pm directive in the pool config.
pm = static: the pool always runs exactlypm.max_childrenworkers, idle or not. Predictable and the lowest per-request latency. Wastes memory if traffic is bursty.pm = dynamic: the pool starts withpm.start_serversworkers and scales betweenpm.min_spare_serversandpm.max_spare_serversidle workers, capped atpm.max_children. The most common default. Good for mixed workloads.pm = ondemand: no workers exist until a request arrives. Workers are spawned on demand and reaped afterpm.process_idle_timeout. Lowest memory footprint, highest cold-start latency. Common on shared hosting where many sites share one server.
Whichever mode is in use, pm.max_children is the hard ceiling. Saturating that ceiling produces the same outcome regardless of mode: the next request has to wait.
What happens to a request that arrives at a saturated pool depends on the listen backlog. PHP-FPM holds incoming requests in a kernel socket queue (sized by listen.backlog), and nginx or Apache holds the connection open while the request is queued. If a worker frees up before nginx's fastcgi_read_timeout is reached, the request is served, just slowly. If the wait exceeds that timeout, nginx returns a 504 Gateway Timeout. If FPM kills the request itself for exceeding request_terminate_timeout, nginx returns a 502 Bad Gateway.
When the pool reaches its ceiling, FPM also writes a line to its error log that looks like this:
WARNING: [pool www] server reached pm.max_children setting (10), consider raising it
That string, documented in the FPM source itself, is the canonical sign of an exhausted pool. If you see it, the pool is too small for the workload, the workload is too heavy for the pool, or both.
Why it was designed this way
A fixed worker ceiling looks restrictive until you consider what the alternative produces. Each PHP worker holds memory: WordPress core, every active plugin, the opcode cache footprint, and any per-request allocation. A typical WordPress worker resident set sits somewhere between 60 MB and 200 MB depending on plugin load. If FPM scaled workers without an upper bound, a traffic spike would let memory grow unchecked until the kernel's OOM killer terminated PHP-FPM (or worse, the database). A capped pool fails predictably (slow requests, queued requests, eventual 502/504) instead of catastrophically (the entire server going dark).
The pool model also isolates one site's bad day from the rest of the server. If a plugin starts running 10-second database queries, those queries occupy workers but cannot starve the underlying machine of memory beyond the ceiling. The host can size the pool against available RAM and know the worst case in advance.
Practical implications for a WordPress site
The number of workers a site can sustain is a function of two things: how much RAM each worker uses and how long each request takes. If a worker uses 120 MB and a server has 4 GB of RAM available for FPM, the upper bound is roughly 33 workers, regardless of how much you want it to be more. If each request takes 200 ms, those 33 workers can serve about 165 requests per second. If each request takes 2 seconds, the same pool serves 16 requests per second. Cutting request time in half is mathematically equivalent to doubling the pool, but does not cost extra memory.
This is why caching dominates the conversation. A page served from a full-page cache (Varnish, nginx fastcgi_cache, an LSCache or WP Rocket disk cache) never reaches PHP-FPM at all. The worker pool is reserved for the requests that genuinely need PHP: logged-in users, WooCommerce cart and checkout, REST API calls, admin actions (see brute force attack protection in WordPress for rate-limiting the login/xmlrpc traffic that fills the pool), and any uncacheable personalized content. A site that serves 95% of its traffic from cache can survive on a small pool. A site where every request is dynamic (membership site, large WooCommerce store with logged-in customers) needs a larger pool or shorter requests.
This is also why the WordPress admin saturates workers first. Every wp-admin request is dynamic, runs a heavier plugin codepath than the front end, and cannot be cached. A handful of admin users can occupy as many workers as hundreds of cached front-end visitors.
When a pool starts to saturate, the visible symptoms are predictable: TTFB rises sharply because requests now include queue time before the first byte is generated, CPU usage climbs because every active worker is doing real work, and the FPM error log starts showing the pm.max_children warning. Visitors see slow pages first, then 502 or 504 responses once requests exceed the configured timeouts.
What PHP workers are NOT
Most confusion about workers comes from conflating them with adjacent concepts. They are none of these things.
- Not WordPress users. Logged-in users do not own a worker. A user's session lives in the database and a cookie. A worker is checked out only for the milliseconds it takes to serve one HTTP request, then returned to the pool. Ten thousand logged-in users do not need ten thousand workers.
- Not visitors. A worker is not "one per visitor". One worker handles many sequential visitors over its lifetime. The metric that matters is concurrent requests, not concurrent users.
- Not CPU cores. Workers and cores are related but not the same. A 4-core server can productively run more than 4 workers when requests are I/O-bound (waiting on the database, on Redis, on an external API), because cores stay free during the wait. A common starting heuristic is 2 to 4 workers per core, but the right number is whatever fits in available RAM and matches the request profile.
- Not database connections. MySQL connections are a separate pool with their own limit (
max_connectionsinmy.cnf). A worker normally holds one DB connection while it runs, but the database has its own ceiling and its own queue. Exhausting workers and exhausting database connections are distinct failure modes with different fixes. - Not a WordPress concept. WordPress has no awareness of workers. The pool is a PHP-FPM construct managed entirely outside WordPress. You cannot raise the worker count from inside WordPress, a plugin cannot expose them, and
wp-config.phphas no setting for them. Configuration lives in/etc/php/<version>/fpm/pool.d/*.confand requires reloading the FPM service to take effect.
Where to go next
If the goal is to configure the pool correctly — choosing the right process manager mode, calculating pm.max_children, and setting the supporting directives — the PHP-FPM tuning guide is the practical companion to this article. If the symptom is the rising server response time that worker saturation produces, the high TTFB article explains how to measure it and what realistic ranges look like. If the symptom is the dashboard hanging while the front end stays fast, the slow WordPress admin article walks through why admin requests are heavier than front-end requests and saturate the pool first. If the symptom is CPU running hot at the same time, the high CPU usage article covers the related causes and signals.