PHP worker pool

On this page 7

A PHP worker pool is a managed group of PHP processes that wait for requests from a web server and execute PHP code, one request per worker at a time. In servers, and especially in server performance, the pool is the main concurrency limit of a PHP application: the number of workers defines how many PHP requests can run simultaneously, and their memory footprint defines how many fit on the machine.

In current deployments the pool is almost always provided by PHP-FPM, the FastCGI Process Manager. A web server such as Nginx or Apache passes each dynamic request over a socket to the pool, a free worker handles it and returns the response, and the worker then stays alive for the next request, keeping OPcache and other warm state in memory.

How a PHP worker pool handles requests

PHP-FPM runs a master process that reads the pool configuration, opens the listening socket and forks child worker processes. Each worker accepts one FastCGI request, runs the script to completion and becomes free again. Because PHP follows a shared-nothing model, request state is discarded after each request, while code compiled into OPcache is kept in shared memory for all workers.

When every worker is busy, new requests wait in the listen backlog of the socket. Short waits are harmless, but a full backlog leads to rejected connections and 502 or 504 errors from the web server. The pool is therefore a queue with a fixed number of servers, and its behavior under load follows queueing logic: once arrival rate exceeds service capacity, latency grows rapidly.

A server can host several pools, each with its own user, socket, limits and settings. Separate pools isolate sites from each other, so a slow or compromised application cannot consume the workers or files of another.

Process manager modes and key directives

PHP-FPM offers three process manager modes, chosen with the pm directive. They differ in how quickly the pool reacts to load and how much memory it holds when idle.

ModeBehaviorBest suited for
staticFixed number of workers always runningDedicated servers with steady load
dynamicKeeps a range of spare workers, grows to a maximumGeneral hosting with variable load
ondemandStarts workers only when requests arriveMany low-traffic sites on one server
PHP-FPM process manager modes

The directives below control pool size and recycling. Their correct values depend on available memory, average worker size and request duration, so the ranges shown are starting points, not universal answers.

DirectiveTypical rangeEffect
pm.max_children5 to 200Hard cap on concurrent PHP requests
pm.start_servers10 to 25% of max_childrenWorkers created at startup in dynamic mode
pm.min_spare_servers / max_spare_servers2 to 20Idle workers kept ready in dynamic mode
pm.max_requests500 to 10,000Recycles a worker to contain memory leaks
pm.process_idle_timeout10 to 60 sEnds idle workers in ondemand mode
request_terminate_timeout30 to 300 sKills requests that run too long
Core pool directives and typical values

Sizing the pool for memory and CPU

The safe upper bound for pm.max_children is the memory available to PHP divided by the average resident memory of one worker. A typical framework application uses roughly 40 to 120 MB per worker, and heavy content management or e-commerce systems may exceed that. Counting shared OPcache memory once, not per worker, gives a more accurate figure.

CPU sets a second limit. If requests are mostly CPU-bound, adding workers beyond about one to two per core only increases context switching. If requests spend much time waiting on databases or remote APIs, more workers per core keep the CPU busy. The right number is where throughput stops rising while latency is still acceptable.

SymptomLikely causeCorrective action
max_children reached warningsPool too small for peak loadRaise max_children if memory allows
Swapping or OOM killsPool too large for available memoryLower max_children or reduce worker memory
High latency, idle CPUWorkers blocked on slow backendsFix slow queries, add caching or timeouts
Worker memory grows steadilyLeaks in extensions or codeSet or lower pm.max_requests
Symptoms of a poorly sized pool

The PHP worker pool interacts closely with other performance mechanisms in the server stack. Understanding the boundaries prevents tuning the wrong layer when a site slows down.

ConceptWhat it limits or speeds upRelation to the pool
ThroughputRequests completed per secondPool size and request time set its ceiling
Connection poolReused database connectionsEach worker may hold its own connection
OPcacheRepeated script compilationShared by all workers, shortens each request
Resource saturationA fully used CPU, memory or I/O resourceOversized pools cause memory saturation
Neighboring concepts in server performance

A frequent consequence is database connection pressure: 150 workers across three servers can open 450 database connections at peak. The database limit must be planned together with the pool size, or a persistent connection pooler placed between them.

Operational pitfalls

  • Copying values between servers without measuring worker memory leads to swapping on smaller machines.
  • Raising max_children to fix slowness often makes it worse when the real bottleneck is the database.
  • Missing request timeouts let a few stuck requests occupy every worker and take the site down.
  • One shared pool for many sites lets a single busy site starve all others.

The status page of PHP-FPM, together with the slow log, shows active and idle workers, the listen queue length and the scripts that run longest. Watching these values during real peaks is the most reliable basis for evidence-based tuning.

The simple explanation

A PHP worker pool is like a team of cashiers in a store. Each cashier serves one customer at a time. With more cashiers, more customers are served at once, and the line moves faster.

But every cashier needs space and a register, and the store has only so much room. Hiring too many means they get in each other’s way. The skill lies in choosing just enough workers for the busiest hour without running out of memory, which is the server’s equivalent of floor space.

How it is used in practice

An online store runs on a server with 8 CPU cores and 16 GB of RAM. Monitoring shows that each PHP worker uses about 90 MB, and the database, web server and operating system need around 5 GB. The administrator reserves 10 GB for PHP and sets pm.max_children to 110, with dynamic mode, 20 start servers and pm.max_requests at 1,000.

Before the change, the pool had been capped at 20 workers, and during a seasonal sale the log filled with max_children warnings while customers saw timeouts. After resizing and adding a 60-second request timeout, the next sale ran with peak usage of about 80 workers, no queued requests and stable memory, while the slow log pointed to one report query that the developers later optimized.