502 / 504 upstream failures
502 Bad Gateway and 504 Gateway Timeout are reported by Nginx but are
almost always caused downstream — PHP-FPM, MySQL, Valkey/Redis, or Drupal
itself. Read the meaning first, then walk the FPM/pool/timeout chain.
| Error | What Nginx saw |
|---|---|
502 Bad Gateway |
FPM accepted the request, then dropped the connection abnormally |
504 Gateway Timeout |
FPM did not respond within fastcgi_read_timeout (_PHP_FPM_TIMEOUT, effective 180 s) |
The Nginx-side internals (vhost templates, the FPM socket plumbing) are in Nginx debug; this page is the downstream-cause procedure.
Is FPM running?
# Every installed PHP version
for v in 5.6 7.1 7.4 8.1 8.3 8.4; do
vv=${v//./}
printf 'PHP-FPM %s: ' "$v"
service "php${vv}-fpm" status 2>/dev/null | head -1
done
Restart any that are down:
service php84-fpm restart
Read the logs
Nginx error log
tail -100 /var/log/nginx/error.log
| Log pattern | Cause |
|---|---|
connect() to unix:/run/*.fpm.socket failed |
FPM pool not running (sockets live under /run/, named <user_socket>.fpm.socket) |
upstream prematurely closed connection |
PHP segfaulted or hit OOM |
upstream timed out (110: Connection timed out) |
504 — request too slow |
PHP-FPM error log
BOA writes one error log per version to a single /var/log/php directory, named
phpNN-fpm-error.log — there is no per-version directory and no php-fpm.log:
tail -100 /var/log/php/php84-fpm-error.log
| Log pattern | Cause |
|---|---|
child <pid> exited on signal 11 (SIGSEGV) |
PHP segfault — usually APCu/opcache; see Recovery & cache faults |
executing too slow |
Slow query / lock — fix Drupal side |
server reached max_children setting |
Pool hit its pm.max_children ceiling — size via the instance plan (below) |
Common 502 causes by frequency
| Cause | Fix |
|---|---|
| APCu / Valkey cache poisoning | drush cr; see Recovery & cache faults |
| FPM pool not running | service phpNN-fpm start |
Long DB query hit _PHP_FPM_TIMEOUT (504) |
Optimise the query; raise timeout temporarily |
| Memory limit exceeded | Raise PHP memory_limit; investigate the leak |
| Valkey/Redis down | Restart the service |
| Disk full | Free disk |
max_children exceeded |
First decide organic vs abuse-driven (below); if organic: dedicated plans auto-size, shared instances raise the plan or pin _PHP_FPM_MAX_CHILDREN_FORCE |
| Loadguard tripped (high CPU) | Check /var/log/boa/high.load.incident.log; wait for load to drop |
Why one busy site 502s its neighbours
All sites under one Octopus instance share a single PHP-FPM pool per PHP
version. When that pool hits pm.max_children, every site on the instance
returns 502 — even idle ones — because they all wait on the same saturated
pool. Dedicated plans (PHANTOM and above) size the pool from box capacity to
absorb bursts; shared plans are capped on purpose. fpmreport shows each pool's
configured ceiling next to its observed peak, so you can see how close a pool
runs to its cap. See
PHP-FPM capacity for the
plan-based sizing model.
Before raising pm.max_children or the plan, decide whether the saturation is
organic capacity or an attack: raising the ceiling absorbs organic growth, but
against abuse-driven saturation it is not the cure — it only moves the
collapse point. Three signals (all in 5.10.3) answer the question:
- Capacity NOTE from the minutely monitor. php.sh logs
NOTE: PHP-FPM reached max_children setting (N new hit(s) in <files>). Consider raising pm.max_children for the affected pool(s). It byte-offset-tails/var/log/php/php*-fpm-error.log, so only new per-pool exhaustion hits since the last run are counted. (Before 5.10.3 this NOTE was dead: it grepped for the globalprocess.maxceiling, which BOA sets to 0 and which therefore never logs.) - FPM-saturation alert + forensic snapshot. Independently, the
scan_nginx scoring engine
watches the same
reached max_children settingsignal (_NGINX_FPM_SAT_DETECT=YESby default) and, on a hit, alerts and snapshots the box-wide top talkers — the who/what behind the exhaustion. - 444s in the access log. If the saturation coincides with a flood of
uncached localized pages (
/<lang>/...requests), the Tier-A request guard caps anonymous localized concurrency at 24 in-flight per vhost (limit_conn boa_i18n_anon, tunable via the provision optionnginx_i18n_anon_conn; logged-in users are never capped) and sheds the excess with444— so a flood shows up as 444s in the access log, not only as 502s. Runfloodreportto answer "was that an i18n flood, and did the guardrail hold?" from/var/xdrago/monitor/log/i18n_flood.logand the snapshots under/var/xdrago/monitor/log/i18n_flood/.
502 on /install.php after Clone-then-Migrate
curl https://foo.example.com/ → redirect to /install.php → /install.php returns 404
Cause: the site's sites/<domain>/settings.php is missing on the new platform
after the migrate. Fix: Verify the site; if Verify also fails, read
/data/disk/<USER>/log/<site>.log; last resort, restore from a known-good
backup.
504 on long admin operations
Cause: an admin task runs longer than _PHP_FPM_TIMEOUT (effective 180 s;
AUTO by default, clamped 60–180 s). Examples: config-import, large feature
revert, mass content migration.
- Raise the timeout in
/opt/etc/fpm/fpm-pool-common*.conf(Operator FAQ). Survives until the nextbarracuda upgrade. - Run the operation via Drush — CLI has no FPM timeout.
Is it actually a firewall block?
If one IP gets 502 but others get 200, the IP may be blocked at the network layer (a different failure mode that looks similar):
curl -I https://foo.example.com/ # from a different IP
If the other IP gets 200 and yours gets 502, it is a firewall block — see IP-blocked recovery.
Tenant note. A
.dev.cache-bypass alias (dev.<domain>) exposes in-browser errors, but it is not auto-present on every site — the operator or tenant must add thedev.<domain>alias to the site first. Hittingdev.foo.example.comon a site without that alias yields no vhost / NXDOMAIN.
Related
- Recovery & cache faults — APCu/Valkey cache-poisoning 502s and the FPM reload sentinel.
- IP-blocked recovery — when a 502 is actually a firewall block.
- Nginx debug — the Nginx-side debug page (sockets, vhost templates).
- PHP-FPM capacity — pool
sizing,
pm.max_children,_PHP_FPM_MAX_CHILDREN_FORCE. - scan_nginx scoring engine — the FPM-saturation trigger and i18n-flood detector (thresholds, snapshots).
- Request guards — the Tier-A anonymous-localized concurrency cap that sheds floods with 444.
- Operator FAQ —
memory_limit, execution time, the FPM common pool files.