skip to content

PHP-FPM

PHP-FPM runs PHP workers behind Nginx or Apache over FastCGI: pools, static/dynamic/ondemand spawning, status page and slow log. Interviewers probe pm.max_children, which trades memory for queueing.

on this pageshow

explore

questions

17

When wiring a PHP-FPM pool to a web server, when should it listen on a Unix socket versus TCP, and how is each secured?

level: middleimportance: must knowfreq 55%

answer

  1. same host vs across hosts or containers
  2. listen.owner, listen.group, listen.mode 0660
  3. permission denied becomes 502
  4. listen.allowed_clients only for TCP
  5. PHP_VALUE lets a client rewrite ini

basics

~20 s

Use a Unix socket when the web server and PHP-FPM share a host, secured by listen.owner, listen.group and listen.mode; use TCP across hosts or containers, restricted with listen.allowed_clients and a firewall, because an exposed FastCGI port lets anyone run PHP.

solid answer

~50 s

The pool's `listen` directive takes either a path, giving a Unix domain socket, or an address and port for TCP. A **Unix socket** works only on the same host, skips the TCP stack, and is protected by file permissions: `listen.owner`, `listen.group` and `listen.mode` (default `0660`, owner defaulting to the user running the FPM master). If the web server's user cannot read and write it, every PHP request fails with a permission error and a 502. **TCP** is needed when the web server runs on another host or container; restrict it with `listen.allowed_clients` (TCP only) and bind to a private address, never a public one. An exposed FastCGI port is remote code execution: a client chooses `SCRIPT_FILENAME` and can pass `PHP_VALUE`/`PHP_ADMIN_VALUE` parameters that rewrite ini settings. Performance differences are small; the choice is about topology and security.

code

ini · 11 lines
ini
; same host as nginx: Unix socket the web server can open
[app]
listen = /run/php/$pool.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

; separate web tier: private TCP address, allow-listed client
[api]
listen = 10.0.1.5:9000
listen.allowed_clients = 10.0.1.10

go deeper

for a junior

Recall that listen takes a socket path or an address and port, and that the web server must be able to connect to whichever you choose.

for a middle

Explain listen.owner, listen.group and listen.mode for sockets, listen.allowed_clients for TCP, and why a wrong socket owner shows up as a 502.

for a senior

Treat an exposed FastCGI port as remote code execution: explain SCRIPT_FILENAME and PHP_VALUE, bind privately, allow-list clients and firewall the port.

for a principal

Choose the topology — co-located sockets, a shared-volume sidecar or a private network — from how the web and PHP tiers are deployed and scaled.

## What listen accepts Each PHP-FPM **pool** accepts FastCGI requests on the address given by its mandatory `listen` directive. The sample `www.conf` accepts four forms: | Form | Example | Meaning | |---|---|---| | `ip:port` | `listen = 127.0.0.1:9000` | TCP on one IPv4 address | | `[ipv6]:port` | `listen = [::1]:9000` | TCP on one IPv6 address | | `port` | `listen = 9000` | TCP on **all** addresses | | path | `listen = /run/php/app.sock` | Unix domain socket | The variable `$pool` expands to the pool name, so `listen = /run/php/$pool.sock` gives each pool its own socket. `listen.backlog` sets the queue of connections waiting for a free worker for either kind. ## Unix domain sockets A Unix socket is a file-system object. It only works between processes on the **same host** (or containers sharing a mounted directory), and it avoids the TCP/IP stack, ports and connection-state tables. Access is controlled by the socket file's ownership and mode: - `listen.owner` and `listen.group` — default to the user running the FPM master (usually root) and that user's group. - `listen.mode` — default `0660`: read and write for owner and group only. - `listen.acl_users` / `listen.acl_groups` — POSIX ACLs; when set, `listen.owner` and `listen.group` are ignored. On Linux the connecting process needs read and write permission, so the web server's user (for example `www-data` or `nginx`) must be the owner, in the group, or listed in the ACL. The classic failure: a hand-written pool with no `listen.owner`, so the socket is `root:root 0660`, the web server gets *permission denied* on connect, and every PHP page returns **502 Bad Gateway**. Set `listen.owner` and `listen.group` explicitly in every pool file you write. ## TCP sockets TCP is required when the web server and FPM are on **different hosts** or in separate containers without a shared volume. It costs a little more per request and, under very high rates, can run into ephemeral-port and TIME_WAIT limits on the web-server side, but for most sites the difference is not measurable. What matters is exposure: 1. **Bind narrowly.** `listen = 9000` binds every interface. Prefer a private address such as `10.0.1.5:9000`, or `127.0.0.1:9000` on one host. 2. **`listen.allowed_clients`** — a comma-separated list of client IPs allowed to connect. It only makes sense with TCP; left blank, **any** address is accepted. 3. **Firewall** the port as well; FPM's own list is a second line, not the only one. ## Why an exposed FastCGI port is critical FastCGI has no authentication. Whoever can connect sends the request parameters, and PHP-FPM trusts them: - `SCRIPT_FILENAME` decides **which file** PHP executes — any PHP file on the server's disk that the pool's user can read and that passes `security.limit_extensions`. - `PHP_VALUE` and `PHP_ADMIN_VALUE` parameters are parsed as ini settings for that request — the second at system level — so an attacker can change settings such as `auto_prepend_file` and turn a request into arbitrary code execution. So a reachable FPM port is equivalent to handing out a shell as the pool user. That is why the sample file binds to `127.0.0.1` and why internet-facing FPM ports are a well-known audit finding. ## Choosing - **Same host, one web server:** Unix socket, with `listen.owner`/`listen.group` set to the web server's user and group. - **Several pools on one host:** one socket per pool, `/run/php/$pool.sock`. - **Web tier and PHP tier on separate machines or containers:** TCP on a private network, `listen.allowed_clients`, firewall rules. - **Container sidecars sharing a volume:** a Unix socket in the shared volume works and removes the network exposure. ## Diagnosing a broken connection Every one of these shows up to the user as **502 Bad Gateway**; the web server's error log tells them apart: - **Connection refused** — nothing listens on that TCP address and port: FPM is down, or the pool listens elsewhere. - **No such file or directory** — the socket path in the web server differs from the pool's `listen`, or FPM is not running (it creates the socket when it starts). - **Permission denied** — the socket exists but its owner, group or mode excludes the web server's user. After changing `listen`, reload FPM and update the web server's FastCGI target to match.

  • After adding a new pool, nginx logs a permission-denied error on the socket and returns 502. What do you check?
    The socket file's owner, group and mode. Without `listen.owner`/`listen.group`, FPM creates it owned by the master's user (usually root) with mode `0660`, so the web server's user cannot connect. Set `listen.owner` and `listen.group` to the web server's user and group, or use `listen.acl_users`, then reload PHP-FPM.
  • Why is `listen = 9000` riskier than `listen = 127.0.0.1:9000`?
    A bare port binds on all addresses, so any host that can route to the machine can speak FastCGI to the pool unless a firewall or `listen.allowed_clients` stops it. A FastCGI client picks `SCRIPT_FILENAME` and can send `PHP_VALUE`/`PHP_ADMIN_VALUE` ini overrides, which amounts to remote code execution.

saying these in an interview costs you the question

  • A Unix socket can be used by a web server on another machine
  • listen.allowed_clients also restricts access to a Unix socket
  • FastCGI authenticates the web server, so an open port is harmless
  • TCP is always much slower, so it should never be used
  • The default socket permissions let any local user connect
open as a page

A ticket-sales PHP site runs on a 4 GB server under PHP-FPM; how do you size pm.max_children from worker memory?

level: middleimportance: must knowfreq 58%

basics

~20 s

Subtract what the OS, web server, database and other services need, then divide the RAM left for PHP by the measured memory of a busy worker. On 4 GB with about 2.5 GB for PHP and 60 MB per worker, that is roughly 40.

open as a page

In a PHP-FPM pool, how do pm = static, pm = dynamic and pm = ondemand differ, and when would you choose each?

level: middleimportance: must knowfreq 60%

basics

~20 s

static keeps exactly pm.max_children workers alive; dynamic starts pm.start_servers and keeps idle workers between the min and max spare counts; ondemand starts none and forks per demand, killing workers idle past pm.process_idle_timeout (10s). pm.max_children caps all three.

open as a page

Every PHP request through Nginx to PHP-FPM returns 'File not found.'; what does SCRIPT_FILENAME do, and what usually went wrong?

level: juniorimportance: should knowfreq 45%

basics

~20 s

SCRIPT_FILENAME is the FastCGI parameter telling PHP-FPM which file to execute. 'File not found.' means FPM could not resolve or open that path — usually the parameter is missing or points to a path that does not exist where FPM runs.

open as a page

Why does getenv('DATABASE_URL') return false in PHP under PHP-FPM when the variable is set in the service's environment?

level: middleimportance: should knowfreq 40%

basics

~20 s

PHP-FPM's clear_env defaults to yes, so workers start with an empty environment plus only the pool's env[] entries. Add env[DATABASE_URL] = $DATABASE_URL to copy it from the master's environment, or set clear_env = no, then reload FPM.

open as a page

In a PHP-FPM pool file, what is the difference between php_value and php_admin_value, and when do you use each?

level: middleimportance: should knowfreq 42%

basics

~20 s

Both set php.ini directives for one PHP-FPM pool. php_value is a default that ini_set() or .user.ini may still change where the directive allows; php_admin_value locks it. Use admin for security and resource limits, plain for defaults.

open as a page

In a PHP-FPM pool with pm = dynamic, how do pm.start_servers, pm.min_spare_servers, pm.max_spare_servers and pm.max_spawn_rate work together?

level: middleimportance: should knowfreq 32%

basics

~10 s

A dynamic PHP-FPM pool starts pm.start_servers workers, then once a second kills one idle worker above pm.max_spare_servers or forks more below pm.min_spare_servers, doubling the spawn rate up to pm.max_spawn_rate (32), never past pm.max_children.

open as a page

What does PHP-FPM's request_terminate_timeout do to a long request, and how does it relate to PHP's max_execution_time?

level: middleimportance: should knowfreq 35%

basics

~20 s

request_terminate_timeout makes the FPM master kill a worker whose request runs past the limit; the client usually gets a 502 and a replacement is forked. Off by default, it backstops PHP's max_execution_time, which raises an error inside the script instead.

open as a page

How do PHP-FPM's request_slowlog_timeout and slowlog work, and what does a slow-log entry show?

level: middleimportance: should knowfreq 38%

basics

~20 s

When a request has run longer than request_slowlog_timeout, the FPM master pauses that worker, reads its PHP call stack and appends it, with the script path, to the pool's slowlog file; the request then continues. It is off by default and needs slowlog set.

open as a page

What do the PHP-FPM status page's active processes, listen queue and max children reached fields tell you about a pool?

level: middleimportance: should knowfreq 42%

basics

~20 s

Active processes counts workers busy right now; listen queue counts connections waiting for a free worker; max children reached counts how often the pool wanted to grow past pm.max_children. Busy workers at the cap plus a non-zero queue means a saturated pool.

open as a page

How would you run two PHP applications under different Unix users behind one Nginx, using PHP-FPM pools?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Define one PHP-FPM pool per application, each with its own user and group, its own listen socket that the Nginx user can open, and locked per-app settings via php_admin_value; each Nginx server block passes PHP requests to its app's socket.

open as a page

During a ticket on-sale, PHP-FPM logs 'server reached pm.max_children setting (40), consider raising it'; what happens to incoming requests, and should you just raise the limit?

level: seniorimportance: should knowfreq 45%

basics

~20 s

The pool is at pm.max_children with idle workers exhausted or below the spare minimum, so new connections queue in the listen backlog until a worker frees up or the web server times out. Raise the cap only with spare memory, CPU and database capacity.

open as a page

What does pm.max_requests do in a PHP-FPM pool, and why is it set to work around memory growth?

level: seniorimportance: should knowfreq 40%

basics

~20 s

pm.max_requests makes each PHP-FPM worker exit after serving that many requests, and the master forks a replacement. The default 0 never recycles. It limits memory that leaks or accumulates inside a long-lived worker, without fixing the cause.

open as a page

During a flash sale, a PHP site behind PHP-FPM returns intermittent 502s; how do you use FPM's status page and logs to find the cause?

level: seniorimportance: should knowfreq 45%

basics

~20 s

A 502 means the web server got no valid FastCGI response. Line up its timestamps with the FPM status page (all workers busy, queue near its length) and the FPM log (terminations, crashes), then read the slow log for what workers wait on.

open as a page

What does PHP-FPM's ping.path endpoint check, and why is it not a full application health check?

level: juniorimportance: nice to knowfreq 22%

basics

~20 s

ping.path makes a PHP-FPM pool answer that URI with a plain-text 'pong' (ping.response) and status 200. It proves the web server can reach the pool and a worker is free, but runs no application code, so it cannot detect a broken database or app.

open as a page

Under PHP-FPM, what does fastcgi_finish_request() do, and what are its pitfalls when used for background work?

level: seniorimportance: nice to knowfreq 25%

basics

~20 s

fastcgi_finish_request() sends all buffered output to the client and ends the HTTP response, while the PHP script keeps running. The worker stays busy, locks such as the session remain held, later output is lost, and the function exists only under PHP-FPM.

open as a page

On a shared host running dozens of PHP-FPM pools, how would you set process manager modes and limits so one busy site cannot starve the rest?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Give each site its own pool with a pm.max_children it may never exceed, use ondemand for mostly idle sites, keep the sum of caps within RAM or overcommit deliberately, and add process.max in php-fpm.conf as a global ceiling.

open as a page