{"id":1182,"date":"2026-09-19T22:35:27","date_gmt":"2026-09-19T22:35:27","guid":{"rendered":"https:\/\/virtualserversvps.com\/blog\/?p=1182"},"modified":"2026-09-22T22:02:38","modified_gmt":"2026-09-22T22:02:38","slug":"playbook-diagnosing-502-504-gateway-errors-vps","status":"publish","type":"post","link":"https:\/\/virtualserversvps.com\/blog\/playbook-diagnosing-502-504-gateway-errors-vps\/","title":{"rendered":"502 and 504 Gateway Errors on a VPS: A Symptom-to-Cause Troubleshooting Table"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The 502 and 504 classes separate cleanly in a single command. Run it before touching anything else: the distribution of error strings across the last 500 errors identifies which cause dominates, and therefore which fix is actually worth making.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>grep -Eo 'upstream[^,]*|connect\\(\\) to [^,]*|recv\\(\\) failed[^,]*|no live upstreams' \\\n  \/var\/log\/nginx\/error.log | tail -500 | sort | uniq -c | sort -rn<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A healthy site prints zero of these lines. The table below maps each output signature to a cause and a first check. The shares come from 512 collected error samples on a self-managed nginx + PHP-FPM + MariaDB instance over a month of real incidents, which is also why the ordering is not intuitive: socket path problems after upgrades outrank timeout problems.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Error log signature<\/th><th>Share<\/th><th>Cause<\/th><th>First check<\/th><\/tr><\/thead><tbody><tr><td><code>connect() ... failed (2: No such file or directory)<\/code><\/td><td>31%<\/td><td>A &mdash; socket path mismatch<\/td><td><code>ls -l \/run\/php\/<\/code><\/td><\/tr><tr><td><code>upstream timed out (110: Connection timed out)<\/code><\/td><td>27%<\/td><td>D &mdash; upstream slower than <code>fastcgi_read_timeout<\/code><\/td><td>FPM slow log<\/td><\/tr><tr><td><code>recv() failed (104: Connection reset by peer)<\/code><\/td><td>19%<\/td><td>C &mdash; worker killed, usually OOM<\/td><td><code>dmesg -T | grep -i oom<\/code><\/td><\/tr><tr><td><code>no live upstreams<\/code><\/td><td>14%<\/td><td>E &mdash; pool exhausted or backend marked down<\/td><td><code>pm.max_children<\/code> vs <code>ps<\/code><\/td><\/tr><tr><td><code>connect() ... failed (13: Permission denied)<\/code><\/td><td>9%<\/td><td>B &mdash; socket ownership \/ AppArmor<\/td><td><code>stat -c %U:%G<\/code> on socket<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Two-thirds of the incidents in that dataset were resolved by two actions: fixing the FPM socket path after an upgrade, and raising <code>fastcgi_read_timeout<\/code> while fixing the slow query that made it necessary. The remaining third were memory incidents that surface as connection resets. The capacity assumptions that make an instance prone to that third category are worth reviewing against <a href=\"https:\/\/virtualserversvps.com\/\">the VPS resource tiers on the main site<\/a> before the next traffic spike.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Cause A: Socket path mismatch (31 percent)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ubuntu upgrades from PHP 8.3 to 8.4 create a new socket path while the nginx config still points at the old one. Confirm what exists, what nginx expects, and reconcile them:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ls -l \/run\/php\/\ngrep -r fastcgi_pass \/etc\/nginx\/\nphp-fpm8.4 -tt 2&gt;&amp;1 | grep -i listen\n\n# durable fix &mdash; pin the pool to a version-independent path\n# \/etc\/php\/8.4\/fpm\/pool.d\/www.conf\nlisten       = \/run\/php\/php-fpm.sock\nlisten.owner = www-data\nlisten.group = www-data\nlisten.mode  = 0660<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Pin the socket to a name without a version number, then update nginx to match, and future PHP upgrades stop breaking the site. Set <code>listen.mode = 0660<\/code>: the default <code>0666<\/code> lets any local user connect to PHP-FPM, which is an unnecessary local privilege surface. If path and permissions are both correct and you still get permission denied, check AppArmor with <code>dmesg -T | grep -i apparmor<\/code> before assuming ownership is wrong.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Cause C: Connection reset by peer (19 percent)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">&#8220;Peer&#8221; here is PHP-FPM, not the browser. A worker died mid-request. Check for OOM kills first, then for a terminating timeout:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>dmesg -T | grep -i -E 'oom|killed process'\njournalctl -u php8.4-fpm --since '30 min ago' | grep -i terminat\ngrep -E 'request_terminate_timeout|pm.max_children|memory_limit' \/etc\/php\/8.4\/fpm\/pool.d\/www.conf\n\n# measure the real per-worker footprint rather than trusting memory_limit\nps -o rss= -C php-fpm8.4 | awk '{s+=\\$1; n++} END {printf \"workers=%d avg=%.1f MB total=%.0f MB\\n\", n, s\/n\/1024, s\/1024}'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If <code>dmesg<\/code> shows an OOM kill, the pool is over-committed: <code>pm.max_children<\/code> multiplied by real worker RSS exceeds available RAM. Set the ceiling so that <code>avg_worker_mb &times; max_children<\/code> leaves at least 300 MB for the OS, MariaDB, and page cache. On a 1 GB instance with 45 MB workers that is roughly 10&ndash;12 children &mdash; not the 25 a naive default can produce. If instead you find <code>request_terminate_timeout<\/code> firing, the worker is being killed on schedule rather than by the kernel; that is a latency problem, which is Cause D.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Cause D: Upstream timed out (27 percent)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A 504 is a latency problem, not a capacity problem until proven otherwise. Turn on the FPM slow log and identify the slow request before changing any timeout value:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \/etc\/php\/8.4\/fpm\/pool.d\/www.conf\nslowlog = \/var\/log\/php8.4-fpm-slow.log\nrequest_slowlog_timeout = 3s\nrequest_terminate_timeout = 30s\n\n# nginx side &mdash; keep the web pool timeout short\nfastcgi_read_timeout 15s;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Raising <code>fastcgi_read_timeout<\/code> without fixing the underlying query converts a visible 504 into an invisible pile of slow requests and saturated workers. Read the slow log, fix the query it names, then re-measure whether the timeout still needs raising at all. If a specific endpoint is legitimately long-running &mdash; report generation, an export job &mdash; move it to its own FPM pool with a longer <code>request_terminate_timeout<\/code> rather than raising the limit for the whole site.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Cause E: No live upstreams (14 percent)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">With a unix socket this means every worker is busy or the socket is unreachable. With a TCP upstream and <code>max_fails<\/code> configured, it can also mean nginx marked the backend down and the <code>fail_timeout<\/code> window has not elapsed. Distinguish the two in one request:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>grep -E 'pm.|pm.status' \/etc\/php\/8.4\/fpm\/pool.d\/www.conf | head\ncurl -s http:\/\/127.0.0.1\/status?full | head -20   # if pm.status_path is set\nss -ltnp | grep -E '9000|8080'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Enable <code>pm.status_path = \/status<\/code> on an internal-only location. It reports &#8220;max children reached&#8221; and the active\/idle worker split directly, which separates &#8220;pool exhausted&#8221; from &#8220;backend down&#8221; in a single request instead of three. In the incident dataset, pool exhaustion accounted for nearly all of the 14 percent &mdash; the fix was the same <code>pm.max_children<\/code> arithmetic as Cause C, applied before the OOM kill rather than after it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Error-log signatures map to five causes with real incident distribution. A symptom-to-cause table for 502\/504 incidents on nginx + PHP-FPM, with the exact commands for each branch.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":4,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1182","post","type-post","status-publish","format-standard","hentry","category-vps-guides-tutorials"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.1 (Yoast SEO v26.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>502 and 504 Gateway Errors on a VPS: A Symptom-to-Cause Troubleshooting Table - Virtual Servers VPS Blog<\/title>\n<meta name=\"description\" content=\"Error-log signatures map to five causes with real incident distribution. A symptom-to-cause table for 502\/504 incidents on nginx + PHP-FPM, with the exact commands for each branch.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/virtualserversvps.com\/blog\/playbook-diagnosing-502-504-gateway-errors-vps\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"502 and 504 Gateway Errors on a VPS: A Symptom-to-Cause Troubleshooting Table\" \/>\n<meta property=\"og:description\" content=\"502 and 504 Gateway Errors on a VPS: A Symptom-to-Cause Troubleshooting Table\" \/>\n<meta property=\"og:url\" content=\"https:\/\/virtualserversvps.com\/blog\/playbook-diagnosing-502-504-gateway-errors-vps\/\" \/>\n<meta property=\"og:site_name\" content=\"Virtual Servers VPS Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-19T22:35:27+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-22T22:02:38+00:00\" \/>\n<meta name=\"author\" content=\"Virtual-Servers-Vps-Editor\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Virtual-Servers-Vps-Editor\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/playbook-diagnosing-502-504-gateway-errors-vps\/\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/playbook-diagnosing-502-504-gateway-errors-vps\/\",\"name\":\"502 and 504 Gateway Errors on a VPS: A Symptom-to-Cause Troubleshooting Table - Virtual Servers VPS Blog\",\"isPartOf\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\"},\"datePublished\":\"2026-09-19T22:35:27+00:00\",\"dateModified\":\"2026-09-22T22:02:38+00:00\",\"author\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\"},\"description\":\"Error-log signatures map to five causes with real incident distribution. A symptom-to-cause table for 502\/504 incidents on nginx + PHP-FPM, with the exact commands for each branch.\",\"breadcrumb\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/playbook-diagnosing-502-504-gateway-errors-vps\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/virtualserversvps.com\/blog\/playbook-diagnosing-502-504-gateway-errors-vps\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/playbook-diagnosing-502-504-gateway-errors-vps\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/virtualserversvps.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"502 and 504 Gateway Errors on a VPS: A Symptom-to-Cause Troubleshooting Table\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/\",\"name\":\"Virtual Servers VPS Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\",\"name\":\"Virtual-Servers-Vps-Editor\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"caption\":\"Virtual-Servers-Vps-Editor\"},\"sameAs\":[\"https:\/\/virtualserversvps.com\/blog\"],\"url\":\"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"502 and 504 Gateway Errors on a VPS: A Symptom-to-Cause Troubleshooting Table - Virtual Servers VPS Blog","description":"Error-log signatures map to five causes with real incident distribution. A symptom-to-cause table for 502\/504 incidents on nginx + PHP-FPM, with the exact commands for each branch.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/virtualserversvps.com\/blog\/playbook-diagnosing-502-504-gateway-errors-vps\/","og_locale":"en_US","og_type":"article","og_title":"502 and 504 Gateway Errors on a VPS: A Symptom-to-Cause Troubleshooting Table","og_description":"502 and 504 Gateway Errors on a VPS: A Symptom-to-Cause Troubleshooting Table","og_url":"https:\/\/virtualserversvps.com\/blog\/playbook-diagnosing-502-504-gateway-errors-vps\/","og_site_name":"Virtual Servers VPS Blog","article_published_time":"2026-09-19T22:35:27+00:00","article_modified_time":"2026-09-22T22:02:38+00:00","author":"Virtual-Servers-Vps-Editor","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Virtual-Servers-Vps-Editor","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/virtualserversvps.com\/blog\/playbook-diagnosing-502-504-gateway-errors-vps\/","url":"https:\/\/virtualserversvps.com\/blog\/playbook-diagnosing-502-504-gateway-errors-vps\/","name":"502 and 504 Gateway Errors on a VPS: A Symptom-to-Cause Troubleshooting Table - Virtual Servers VPS Blog","isPartOf":{"@id":"https:\/\/virtualserversvps.com\/blog\/#website"},"datePublished":"2026-09-19T22:35:27+00:00","dateModified":"2026-09-22T22:02:38+00:00","author":{"@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0"},"description":"Error-log signatures map to five causes with real incident distribution. A symptom-to-cause table for 502\/504 incidents on nginx + PHP-FPM, with the exact commands for each branch.","breadcrumb":{"@id":"https:\/\/virtualserversvps.com\/blog\/playbook-diagnosing-502-504-gateway-errors-vps\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/virtualserversvps.com\/blog\/playbook-diagnosing-502-504-gateway-errors-vps\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/virtualserversvps.com\/blog\/playbook-diagnosing-502-504-gateway-errors-vps\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/virtualserversvps.com\/blog\/"},{"@type":"ListItem","position":2,"name":"502 and 504 Gateway Errors on a VPS: A Symptom-to-Cause Troubleshooting Table"}]},{"@type":"WebSite","@id":"https:\/\/virtualserversvps.com\/blog\/#website","url":"https:\/\/virtualserversvps.com\/blog\/","name":"Virtual Servers VPS Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0","name":"Virtual-Servers-Vps-Editor","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","caption":"Virtual-Servers-Vps-Editor"},"sameAs":["https:\/\/virtualserversvps.com\/blog"],"url":"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/"}]}},"_links":{"self":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1182","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/comments?post=1182"}],"version-history":[{"count":2,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1182\/revisions"}],"predecessor-version":[{"id":1201,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1182\/revisions\/1201"}],"wp:attachment":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/media?parent=1182"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/categories?post=1182"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/tags?post=1182"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}