PHP-FPM

On this page 7

PHP-FPM (FastCGI Process Manager) is the standard process manager for running PHP behind a web server. It keeps pools of PHP worker processes alive and hands them requests over the FastCGI protocol, so web servers such as Nginx, Apache, Caddy and OpenLiteSpeed can serve static files themselves and pass only PHP requests to a separate, independently tuned service.

PHP-FPM has been part of the official PHP distribution since version 5.3.3 and is the default way to run PHP in modern web server stacks. Its pool model gives administrators direct control over concurrency, memory use and isolation between sites.

How PHP-FPM handles requests

PHP-FPM runs as a master process with child worker processes. The master reads the configuration, opens the listening sockets and starts, stops and recycles workers. Each worker handles one request at a time: it receives the script path and request variables from the web server, executes the PHP code and returns the response over the same connection.

The web server and PHP-FPM communicate through a Unix domain socket or a TCP socket. A Unix socket such as /run/php/php8.3-fpm.sock avoids network overhead when both run on the same host, while TCP, typically on port 9000, allows PHP-FPM to run on separate servers or containers.

Workers are long-lived, so expensive initialization happens only once. OPcache keeps compiled PHP bytecode in shared memory across all workers of the master, which removes the cost of parsing scripts on every request and is the single most important performance setting for PHP-FPM.

Pools and process manager modes

A pool is a group of workers with its own socket, user account, limits and PHP settings. Running each site in a separate pool under its own system user isolates sites from each other, so a compromised or overloaded site does not affect its neighbors. Every pool chooses one of three process manager modes.

ModeBehaviorBest forTrade-off
staticKeeps a fixed number of workers runningBusy dedicated servers with steady loadMemory is reserved even when idle
dynamicScales workers between spare-server limitsGeneral-purpose serversNeeds four settings tuned together
ondemandStarts workers only when requests arriveMany low-traffic sites on one hostFirst request after idle is slower
PHP-FPM process manager modes

The settings below control each pool. Their values depend on available memory and the memory used per worker, which ranges from about 30 MB for simple scripts to several hundred MB for heavy applications.

SettingPurposeTypical starting point
pm.max_childrenMaximum concurrent workersUsable RAM divided by average worker size
pm.start_serversWorkers started at launch (dynamic)About 25 percent of max_children
pm.min_spare_servers / pm.max_spare_serversIdle worker range (dynamic)Small range around start_servers
pm.max_requestsRequests before a worker is recycled500-1000 to contain memory leaks
request_terminate_timeoutHard limit per requestSlightly above the web server timeout
Key pool settings

PHP-FPM compared with other ways to run PHP

Before PHP-FPM became standard, PHP usually ran embedded inside Apache as mod_php. Other approaches exist today as well. The table places PHP-FPM among them.

ApproachProcess modelIsolation per siteTypical pairing
PHP-FPMSeparate FastCGI worker poolsStrong, separate users per poolNginx, Apache event MPM, Caddy
mod_phpPHP inside every Apache processWeak, one Apache userApache prefork MPM
LSAPILiteSpeed-specific PHP processesStrong with suEXECOpenLiteSpeed and LiteSpeed
Application serversPersistent PHP application in memoryPer applicationModern PHP frameworks with workers
Ways to run PHP behind a web server

Compared with mod_php, PHP-FPM lets Apache use the efficient event MPM and keeps static file requests away from heavy PHP processes. Compared with plain CGI, it avoids starting a new process for every request. FastCGI itself is the protocol; PHP-FPM is the implementation that manages PHP workers speaking it.

Tuning PHP-FPM in practice

Tuning starts with measuring real worker memory under production load, not with copying values from another server. Multiply the average worker size by pm.max_children and confirm that the total, plus the database, cache and operating system, fits in RAM without swapping.

The status page (pm.status_path) reports active and idle workers, the listen queue and the counter max children reached. A non-zero listen queue or a growing max children counter means requests are waiting; the slow log (request_slowlog_timeout) records stack traces of requests that exceed a threshold, which identifies the code responsible.

ObservationLikely meaningAction
Max children reached growsPool too small for peak loadRaise max_children if memory allows
Server swaps under loadToo many workers for RAMLower max_children or optimize code
Workers slowly grow in memoryLeaks in application or extensionsLower pm.max_requests
Many slow log entriesSlow queries or external callsFix the code path, not the pool size
Tuning decisions

Common PHP-FPM errors and misconceptions

Most visible PHP-FPM failures appear first as web server errors. A 502 Bad Gateway usually means the socket is missing or permissions prevent the web server from connecting; a 504 Gateway Timeout means a worker took longer than the web server allowed. Both point to PHP-FPM, but the cause differs.

A frequent misconception is that more workers always mean more capacity. Each worker is bound by CPU and by slow backends; when the database is the bottleneck, extra workers only add contention. Another mistake is sharing one pool among unrelated sites, which removes isolation and makes per-site limits impossible.

In simple terms

PHP-FPM is a team of workers waiting to run PHP code. When a visitor opens a page built with PHP, the web server does not run the code itself. It passes the job to one of these waiting workers, gets the finished page back and sends it to the visitor.

The administrator decides how many workers each website gets. Too few, and visitors wait in line; too many, and the server runs out of memory.

A practical example

An agency hosts eight WordPress sites on one server with 8 GB of RAM, using Nginx and PHP-FPM. During a campaign, one shop site becomes slow and returns occasional 504 errors, while the other sites stay fast.

The status page of the shop’s pool shows max children reached increasing every few minutes. Workers use about 90 MB each, and the pool is limited to 10 children. The slow log reveals that a product filter waits on an unindexed database query.

The team adds the missing index and raises the shop pool to 20 workers in dynamic mode, staying within memory because the other sites run in ondemand pools. Response times drop below one second, and the separate pools keep the other sites unaffected throughout the campaign.