The nginx error connect() to unix:/run/php/php-fpm.sock failed (11: Resource temporarily unavailable) while connecting to upstream looks like a connection problem. It is not. Errno 11 is EAGAIN. nginx uses non-blocking sockets, and in this context the kernel’s accept queue for the PHP-FPM Unix socket is full. The kernel had nowhere to buffer the new FastCGI connection, so it refused the connect(2) immediately.
This is a saturation error, not a connection error. PHP-FPM is alive, the socket file exists, the master is listening, and systemctl status php-fpm will look healthy. The pool is out of workers, and the backlog buffer between nginx and PHP-FPM has filled.
Do not raise listen.backlog on its own. A larger backlog only lengthens the queue of requests that will eventually time out. The real fixes are the standard pool-exhaustion remedies: add workers if RAM permits, make requests faster, or shed traffic. Backlog tuning buys seconds of buffer at best; it does not add throughput.
What this means
PHP-FPM listens on a Unix domain socket (or a TCP socket). Between nginx and PHP-FPM’s workers sits a kernel-managed accept queue, sized by listen.backlog in the pool config and clamped to min(listen.backlog, net.core.somaxconn).
When nginx opens a FastCGI connection, the kernel places it in that accept queue. The PHP-FPM master pulls connections off the queue and hands them to idle workers. Each worker handles exactly one request at a time. Maximum concurrent request capacity equals the number of active workers, bounded by pm.max_children.
When every worker is busy, the queue fills. When the queue reaches its maximum, the kernel refuses new connect(2) calls from non-blocking sockets with EAGAIN (errno 11). nginx logs the connect failure and returns 502 Bad Gateway.
flowchart LR A[nginx] -->|connect| B[kernel accept queue] B -->|accept| C[FPM master] C -->|dispatch| D[worker pool] D -->|all busy| E[max_children reached] E -->|queue fills| F[connect EAGAIN] F -->|502| G[user]
This is distinct from errno 111 (Connection refused), which means nothing is listening on the socket. With EAGAIN, something is listening, but the queue is full. The PHP-FPM master is up, the socket file exists, and the process looks healthy. Only the kernel and nginx know the queue is saturated.
Two gotchas around the backlog value itself:
- On Linux, setting
listen.backlog = -1in the pool config (the PHP manual says this means “unlimited” on BSD) is silently treated as too low and replaced with 128. PHP-FPM logs a warning such aslisten.backlog(-1) was too low for the ondemand process manager. I updated it for you to 128. The documented “-1 means maximum” behavior applies to BSD, not Linux. - The effective backlog on Linux is
min(listen.backlog, net.core.somaxconn). Even an explicit large value can be capped by the kernel default. Checkcat /proc/sys/net/core/somaxconn; the default varies by distribution (128 on older distributions, 4096 on modern kernels).
The PHP-FPM default for listen.backlog changed across versions: 511 on Linux for PHP releases before 8.2, and -1 (clamped to somaxconn) on PHP 8.2 and later. Either way, the kernel has the final say on the effective size.
Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| All workers busy on a slow backend | active processes pinned at max_children, listen queue growing, CPU low | Slow log for stack traces blocked on DB or external API |
Traffic burst beyond pm.max_children | Rapid climb in accepted connection rate, idle processes hits zero | Accepted conn rate against baseline |
| Effective backlog clamped low | ss -xlnp Send-Q is 128 or 511 while workers still have headroom | net.core.somaxconn and listen.backlog in pool config |
ondemand cold-start lag | Spike after idle period, workers spawning slowly | pm mode and pm.process_idle_timeout |
| DDoS or retry storm | Many connections from a small set of IPs, error rate spikes | nginx access log for IP concentration |
Quick checks
# Socket exists and master is listening. For the listen socket:
# Send-Q = effective backlog capacity (after somaxconn clamp)
# Recv-Q = current queued connections (near Send-Q means queue is full)
ss -xlnp | grep php
# PHP-FPM status page: current queue depth, peak since start, configured len
# Path depends on pm.status_path in pool config
curl -s http://127.0.0.1/fpm-status | grep "listen queue"
# Confirm workers are at the ceiling
curl -s http://127.0.0.1/fpm-status | grep -E "^(active|idle|total) processes"
# Counter of times pm.max_children blocked a spawn (dynamic and ondemand only)
curl -s http://127.0.0.1/fpm-status | grep "^max children reached"
# nginx EAGAIN errors scoped to the current hour
grep "$(date '+%Y/%m/%d %H')" /var/log/nginx/error.log | grep -c "Resource temporarily unavailable"
# Kernel-level accept queue overflows and drops (cumulative counters)
nstat | grep -iE "ListenOverflows|ListenDrops"
# Effective somaxconn clamp
cat /proc/sys/net/core/somaxconn
# Per-process FD limit for the master (workers hitting EMFILE will fail
# to open database/API connections, producing slow requests)
cat /proc/$(pgrep -f 'php-fpm: master' | head -1)/limits | grep "Max open files"
All of these are read-only and safe to run during an incident.
How to diagnose it
Confirm
EAGAIN, not Connection refused. The nginx error string contains(11: Resource temporarily unavailable). Errno 111 would read(111: Connection refused)and points to a different failure path: no listener, dead master, wrong socket path. See the related guide on connection refused for that.Inspect the listen socket with
ss -xlnp. TheSend-Qcolumn shows the kernel’s actual backlog capacity after thesomaxconnclamp. TheRecv-Qcolumn shows the current number of connections waiting in the accept queue. WhenRecv-Qsits at or nearSend-Q, the queue is full and the kernel is refusing new connects.Pull the PHP-FPM status page. The three fields that matter for this symptom are
listen queue(current snapshot),max listen queue(peak since master start), andlisten queue len(the backlog PHP-FPM passed tolisten()). A non-zeromax listen queuemeans the pool has experienced queuing even if the current value is zero.Check
active processesagainstpm.max_children. If active is pinned atmax_children, the pool is exhausted. Ifidle processesis zero indynamicorstaticmode, you are one burst away fromEAGAIN.Look at
max children reached. If the counter is incrementing, the process manager wanted to spawn more workers but hit the ceiling. This is the smoking gun for pool exhaustion, and it is only meaningful fordynamicandondemandmodes (always zero forstatic).Read the slow log. If
request_slowlog_timeoutis configured (it defaults to 0, meaning disabled), recent entries show which script and which call stack is holding workers. The slow log is what tells you whether this is a traffic problem or a slow-backend problem.Cross-check the kernel drop counters.
nstat | grep -iE 'ListenOverflows|ListenDrops'shows connections the kernel refused because the accept queue was full. These never appear in the PHP-FPM status page. PHP-FPM has no visibility into kernel-level drops.Inspect per-worker state. The full status page (
/fpm-status?full) shows each worker’s currentrequest URI,request duration, andlast request cpu. Workers stuck on the same URI for seconds are typically blocked on a backend dependency. Workers showing high CPU are compute-bound.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
listen queue (status page) | Earliest direct signal that workers cannot keep up | Any sustained non-zero value |
max listen queue | High-water mark since master start; catches spikes between polls | Approaches listen queue len |
Recv-Q on the listen socket | Kernel-level confirmation of queue depth | Near Send-Q |
active processes / max_children | Pool utilization ratio | Sustained above 0.8 |
idle processes | Burst headroom | Zero in dynamic or static mode |
max children reached rate | Times the PM wanted more workers but could not spawn | Incrementing during normal traffic |
slow requests rate | Workers stuck past request_slowlog_timeout | Any non-zero rate |
ListenOverflows / ListenDrops | Connections the kernel refused at the socket | Non-zero and increasing |
| nginx 502 rate | User-visible impact | Any non-zero during real traffic |
Fixes
Add workers, if RAM permits
The direct fix. Raise pm.max_children in the pool config and reload:
systemctl reload php-fpm
# or: kill -USR2 $(pgrep -f 'php-fpm: master')
Before doing this, verify memory headroom. A common sizing heuristic: memory-safe max_children = (total_RAM * 0.7 - OS_overhead) / avg_worker_RSS. Use PSS via smem or /proc/<pid>/smaps_rollup for the per-worker number, not raw RSS, because RSS double-counts the opcache shared segment across forked workers and overstates unique memory by roughly 30 to 50 percent.
The reload itself has a brief no-worker window. SIGUSR2 drains existing workers before the master re-execs, so expect a short spike in EAGAIN errors during the reload.
Reduce request duration
If the slow log points to a backend (database, external API, NFS, Redis), the bottleneck is not PHP-FPM. Adding workers only buys time before the next saturation event. Fix the slow query, tighten the cURL timeout, add a circuit breaker, or shed the slow endpoint at the nginx layer.
A single request that takes 5 seconds instead of 50ms holds a worker 100x longer. A handful of slow endpoints can quietly consume most of the pool while aggregate traffic looks normal.
Shed traffic at the web server
If this is a burst, DDoS, or retry storm, the right response is rate limiting or IP blocking at nginx, not bigger pools. Look at the access log for IP concentration, request URI patterns, and user-agent strings. The pool will refill once traffic subsides.
Do not just raise listen.backlog
The backlog is a buffer, not throughput. A larger backlog lets more connections pile up waiting, but they still need a worker eventually. If the pool stays saturated for seconds, a bigger backlog just means more requests that hit nginx’s fastcgi_read_timeout and return 504 instead of 502. You have moved the failure mode, not fixed it.
The default listen.backlog (511 on Linux for PHP releases before 8.2, -1 clamped to somaxconn on PHP 8.2+) is sufficient for almost every workload. The one reasonable kernel-side change is raising net.core.somaxconn if it is still at the legacy 128 default on an older distribution. Treat that as a small buffer improvement, not a saturation fix.
ondemand cold-start
If pm = ondemand, idle workers are killed after pm.process_idle_timeout (default 10s). A burst after idle must wait for fork plus PHP initialization before workers can accept. The error log may show listen.backlog(-1) was too low for the ondemand process manager as a secondary symptom. Consider switching to dynamic or static for predictable headroom, or accept the cold-start latency as the cost of lower idle memory.
Prevention
- Monitor the
listen queuefield on the status page. It gives minutes of advance warning before the kernel starts dropping connections. By the time nginx logsEAGAIN, you are already in the failure state. - Set
pm.max_requeststo 500-1000. Without worker recycling, memory leaks accumulate and eventually trigger OOM kills that look like saturation. - Enable
request_slowlog_timeout(for example 5 seconds). It is the single signal that tells you why workers are busy, not just that they are. It defaults to 0 (disabled) in many distributions. - Track
active processes / max_childrenas a leading indicator. Capacity target: peak utilization at or below 75% ofmax_children. - Verify
net.core.somaxconnis not still at the legacy 128 default if your distribution ships that. A modern default removes one rare clamp. - If your pool config sets
listen.backlog = -1, confirm what PHP-FPM actually applied on Linux.-1is not unlimited on Linux; it is silently replaced. - Poll the status page at 1-second intervals for operational alerting. A 10 to 30 second interval will miss transient saturation events.
How Netdata helps
- Per-second collection of PHP-FPM status page fields (
active processes,idle processes,listen queue,max children reached,listen queue len) makes the saturation form visible in real time, not between 10-second polls that miss transient queue build-ups. - Anomaly ML on
active processesandlisten queuecatches the climb beforeEAGAINreaches nginx, without requiring static thresholds that differ per workload. - Same-timeline correlation of nginx 502/504 error rate, PHP-FPM pool saturation, and kernel
ListenOverflowsnarrows the cause from a single dashboard view rather than across three tools. - Per-pool dashboards for multi-pool deployments. A single saturated pool behind a round-robin load balancer shows up as every-Nth-request slowness unless each pool is tracked independently.
- Container cgroup-level memory tracking catches the OOM-kill variant of this symptom before PHP-FPM’s own logs surface it, which matters in container deployments where the master can be killed without a syslog entry.
Related guides
- PHP-FPM 502 Bad Gateway: the web server cannot reach the pool
- PHP-FPM 504 Gateway Timeout: requests accepted but never finishing in time
- PHP-FPM active processes near max_children: reading pool utilization
- PHP-FPM in containers: cgroup limits and the silent OOM kill
- PHP-FPM “child N exited on signal 11 (SIGSEGV)”: worker segfaults
- PHP-FPM “connect() failed (111: Connection refused) while connecting to upstream”
- PHP-FPM crash loop and fork storm: workers dying faster than they serve
- PHP-FPM dynamic mode scaling lag: why the pool cannot keep up with bursts
- PHP-FPM emergency restart: “failed processes threshold reached, initiating reload”
- PHP-FPM graceful reload: the brief no-worker window on SIGUSR2
- How PHP-FPM actually works in production: a mental model for operators
- PHP-FPM idle processes at zero: no burst headroom left






