Fixing Nginx 502 Bad Gateway with PHP-FPM Unix Socket & TCP Pools (Ubuntu, Debian, Alpine, WSL2)
Comprehensive troubleshooting guide for connect() to unix:/var/run/php/php-fpm.sock failed (111: Connection refused). Learn root causes, system traces, and verified resolutions.
Comprehensive troubleshooting guide for connect() to unix:/var/run/php/php-fpm.sock failed (111: Connection refused). Learn root causes, system traces, and verified resolutions.
A "502 Bad Gateway" error indicates that Nginx, acting as a reverse proxy, failed to receive a valid response from an upstream server. In the context of a PHP application served by Nginx on Alpine Linux, this almost universally means Nginx was unable to communicate correctly with the PHP-FPM (FastCGI Process Manager) service. When configured to use a Unix domain socket, this communication breakdown is frequently due to misconfigurations related to the socket path, file permissions, or the PHP-FPM service itself. This guide will walk you through diagnosing and resolving these common issues.
<!-- Client Box -->
<rect x="35" y="90" width="130" height="110" rx="6" fill="#151926" stroke="#2d3748" stroke-width="1.5"/>
<text x="100" y="135" fill="#f3f4f6" font-size="14" font-weight="700" text-anchor="middle">HTTP Client</text>
<text x="100" y="160" fill="#06b6d4" font-size="11" font-family="'JetBrains Mono', monospace" text-anchor="middle">GET /index.php</text>
<!-- Arrow 1 -->
<path d="M 165 145 L 225 145" stroke="#06b6d4" stroke-width="2"/>
<!-- Nginx Web Server -->
<rect x="230" y="70" width="180" height="155" rx="6" fill="#151926" stroke="#06b6d4" stroke-width="1.5"/>
<text x="320" y="100" fill="#06b6d4" font-size="14" font-weight="700" text-anchor="middle">Nginx Web Server</text>
<text x="320" y="122" fill="#9ca3af" font-size="11" text-anchor="middle">fastcgi_pass directive</text>
<rect x="245" y="140" width="150" height="65" rx="4" fill="#0b0d14" stroke="#2d3748" stroke-width="1"/>
<text x="320" y="162" fill="#f3f4f6" font-size="11" font-weight="600" text-anchor="middle">Proxy Gateway</text>
<text x="320" y="180" fill="#f87171" font-size="10" font-family="'JetBrains Mono', monospace" text-anchor="middle">502 Bad Gateway</text>
<text x="320" y="195" fill="#9ca3af" font-size="9" text-anchor="middle">(if socket is closed)</text>
<!-- Arrow 2 Socket -->
<path d="M 410 145 L 485 145" stroke="#f59e0b" stroke-width="2" stroke-dasharray="4,4"/>
<text x="448" y="135" fill="#f59e0b" font-size="10" font-family="'JetBrains Mono', monospace" text-anchor="middle">.sock / :9000</text>
<!-- PHP-FPM / Upstream Daemon -->
<rect x="490" y="70" width="265" height="155" rx="6" fill="#151926" stroke="#10b981" stroke-width="1.5"/>
<text x="622" y="100" fill="#10b981" font-size="14" font-weight="700" text-anchor="middle">PHP-FPM / Application Pool</text>
<text x="622" y="122" fill="#9ca3af" font-size="11" text-anchor="middle">Unix Domain Socket / TCP Pool</text>
<rect x="505" y="140" width="235" height="65" rx="4" fill="#0b0d14" stroke="#10b981" stroke-width="1"/>
<text x="622" y="162" fill="#34d399" font-size="11" font-family="'JetBrains Mono', monospace" text-anchor="middle">/run/php/php8.x-fpm.sock</text>
<text x="622" y="180" fill="#9ca3af" font-size="10" text-anchor="middle">Owner: www-data:www-data (0660)</text>
<text x="622" y="195" fill="#a7f3d0" font-size="9" text-anchor="middle">pm.max_children / Worker Execution</text>
Figure 2: FastCGI socket communication and upstream pool permission requirements.
Symptom & Error Signature
When encountering this issue, users will typically see a generic "502 Bad Gateway" page in their web browser. On the server side, the critical information is found in the Nginx error logs.
Browser Output:
502 Bad Gateway
nginx/1.24.0
Nginx Error Log (/var/log/nginx/error.log or Docker container logs):
You'll typically find one of these entries, providing a clue to the root cause:
- Socket not found/PHP-FPM not running:
2023/10/26 10:30:05 [crit] 1#1: *1 connect() to unix:/var/run/php/php7.4-fpm.sock failed (2: No such file or directory) while connecting to upstream, client: 192.168.1.100, server: example.com, request: "GET /index.php HTTP/1.1", upstream: "fastcgi://unix:/var/run/php/php7.4-fpm.sock:", host: "example.com" - Permission denied:
2023/10/26 10:30:05 [crit] 1#1: *1 connect() to unix:/var/run/php/php7.4-fpm.sock failed (13: Permission denied) while connecting to upstream, client: 192.168.1.100, server: example.com, request: "GET /index.php HTTP/1.1", upstream: "fastcgi://unix:/var/run/php/php7.4-fpm.sock:", host: "example.com" - PHP-FPM process prematurely closed connection (often due to crashes or timeouts):
2023/10/26 10:30:05 [error] 1#1: *1 upstream prematurely closed connection while reading response header from upstream, client: 192.168.1.100, server: example.com, request: "GET /index.php HTTP/1.1", upstream: "fastcgi://unix:/var/run/php/php7.4-fpm.sock:", host: "example.com"
Root Cause Analysis
The "502 Bad Gateway" error with Nginx and PHP-FPM via a Unix socket on Alpine Linux usually boils down to one or more of these underlying issues:
- PHP-FPM Service Not Running: The most straightforward cause. If PHP-FPM isn't active, the Unix socket won't exist, and Nginx has nowhere to connect.
- Incorrect Unix Socket Path: A mismatch between the socket path Nginx is configured to use (
fastcgi_pass) and the path PHP-FPM is configured to listen on (listen). - Socket Permissions: Nginx runs under a specific user (e.g.,
nginx). This user must have read and write permissions to the PHP-FPM Unix socket file, and execute permissions on all parent directories leading to the socket. Alpine Linux often uses thenginxuser by default for Nginx, andwww-dataor a similar user for PHP-FPM. - PHP-FPM Pool Configuration Issues: The
listen.owner,listen.group, andlisten.modedirectives in the PHP-FPM pool configuration file (www.confor similar) are crucial for setting correct socket permissions. - Resource Exhaustion or PHP Errors: PHP-FPM processes might be crashing due to memory limits, fatal PHP errors, or script timeouts (
request_terminate_timeout), causing PHP-FPM to close the connection before Nginx receives a full response. This often manifests as "upstream prematurely closed connection". - Nginx User Mismatch: The user Nginx is running as does not belong to the group that owns the PHP-FPM socket, or vice-versa, preventing proper access.
- Docker-specific Issues: In a Dockerized environment, bind mounts for the socket might be misconfigured, or processes inside the container might not be starting correctly.
Step-by-Step Resolution
Follow these steps to diagnose and resolve the 502 Bad Gateway error. Remember to check Nginx and PHP-FPM logs after each change.
1. Verify PHP-FPM Service Status
First, ensure PHP-FPM is actually running and listening for connections. Alpine Linux often runs in containers or uses OpenRC as an init system.
For Docker Containers:
docker ps | grep php-fpm docker logs <php-fpm-container-id-or-name>If
php-fpmis not listed indocker ps, or logs show startup failures, restart the container:docker restart <php-fpm-container-id-or-name>For Alpine with OpenRC (bare metal or VM):
rc-service php-fpm status rc-service php-fpm startGeneral process check (works everywhere):
ps aux | grep php-fpmYou should see several
php-fpm: pool wwwprocesses running. If not, PHP-FPM isn't active.If PHP-FPM isn't running, check its startup configuration and logs (e.g.,
/var/log/php-fpm.logor container logs) for specific errors preventing it from starting.
2. Confirm Socket Path Consistency
A common mistake is having Nginx configured to use one socket path while PHP-FPM is configured to listen on another.
- Check Nginx configuration:
Locate your Nginx server block configuration for the site (e.g.,
/etc/nginx/conf.d/default.confor/etc/nginx/http.d/default.conf). Find thefastcgi_passdirective:# /etc/nginx/conf.d/default.conf server { # ... location ~ .php$ { fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; # <-- Note this path fastcgi_index index.php; include fastcgi.conf; } # ... } - Check PHP-FPM pool configuration:
Locate your PHP-FPM pool configuration file (e.g.,
/etc/php8/php-fpm.d/www.confon Alpine, replacephp8with your PHP version). Find thelistendirective:; /etc/php8/php-fpm.d/www.conf [www] listen = /var/run/php/php7.4-fpm.sock ; <-- This path must match NginxEnsure both paths are identical, including the exact PHP version in the socket name if specified.
3. Inspect Socket Permissions
This is a frequent culprit for (13: Permission denied) errors. Nginx needs appropriate permissions to access the socket file.
Check the socket file and its parent directory:
ls -l /var/run/php/php7.4-fpm.sock ls -ld /var/run/php/Expected output might look like:
srw-rw---- 1 www-data www-data 0 Oct 26 10:45 /var/run/php/php7.4-fpm.sock drwxr-xr-x 2 root root 60 Oct 26 10:45 /var/run/php/Here, the socket is owned by
www-data:www-data. Nginx, typically running as thenginxuser, would need to be part of thewww-datagroup or have appropriatelisten.modesettings.Check Nginx user: Inspect your main Nginx configuration file (
/etc/nginx/nginx.conf) for theuserdirective:# /etc/nginx/nginx.conf user nginx; # <-- Nginx runs as this user worker_processes auto; # ...Adjust PHP-FPM
listendirectives: In your PHP-FPM pool configuration (/etc/php8/php-fpm.d/www.conf), modify thelisten.owner,listen.group, andlisten.modedirectives to grant Nginx access.Option A: Add Nginx user to PHP-FPM group (Recommended): Ensure the
nginxuser is a member of the group that owns the PHP-FPM socket (e.g.,www-data).# On Alpine with OpenRC adduser nginx www-data # Or in Dockerfile: # RUN addgroup -S www-data && adduser -S nginx www-dataThen, configure PHP-FPM to create the socket with the group ownership and mode:
; /etc/php8/php-fpm.d/www.conf listen = /var/run/php/php7.4-fpm.sock listen.owner = www-data listen.group = www-data listen.mode = 0660 ; Allows owner and group to read/writeOption B: Grant broader permissions (less secure, but sometimes necessary in constrained environments):
; /etc/php8/php-fpm.d/www.conf listen = /var/run/php/php7.4-fpm.sock ; listen.owner = www-data ; Optional, if not needed to be explicitly www-data ; listen.group = www-data ; Optional listen.mode = 0666 ; Allows anyone to read/write (use with caution!)After changing PHP-FPM configuration, you must restart PHP-FPM for changes to take effect. If you're modifying users/groups, a full restart of both Nginx and PHP-FPM is recommended.
4. Review Nginx & PHP-FPM Configuration Files
Incorrect or insufficient settings can lead to premature connection closure.
Nginx Configuration (
/etc/nginx/nginx.confor site-specific conf): Ensurefastcgi_buffer_size,fastcgi_buffers, andfastcgi_read_timeoutare adequate, especially for larger PHP scripts or slower operations.# In your Nginx http or server block http { # ... fastcgi_buffers 16 16k; # Increase buffer size fastcgi_buffer_size 32k; fastcgi_connect_timeout 5s; # How long Nginx waits to connect to FPM fastcgi_send_timeout 120s; # How long Nginx waits for FPM to send data fastcgi_read_timeout 120s; # How long Nginx waits for FPM to send a response # ... }PHP-FPM Pool Configuration (
/etc/php8/php-fpm.d/www.conf): Review resource limits. "Upstream prematurely closed connection" can be a symptom of PHP-FPM processes dying.; /etc/php8/php-fpm.d/www.conf request_terminate_timeout = 60s ; How long a single PHP script can run ; If set to 0, it means 'Off'. Set to a reasonable value. catch_workers_output = yes ; Important for debugging, sends PHP errors to PHP-FPM logs ; Process Manager settings (adjust based on server resources) pm = dynamic ; or ondemand, static pm.max_children = 50 ; Max number of FPM child processes pm.start_servers = 5 pm.min_spare_servers = 5 pm.max_spare_servers = 35Also, check
php.ini(/etc/php8/php.ini) formemory_limitandmax_execution_time. If scripts exceed these, PHP processes can crash.
5. Examine PHP-FPM Logs for Errors
PHP-FPM logs are invaluable for understanding why processes might be failing.
tail -f /var/log/php-fpm.log
# Or for Docker:
docker logs -f <php-fpm-container-id-or-name>
Look for FATAL, ERROR, or WARNING messages, especially those related to memory_limit, max_execution_time, or uncaught exceptions in your PHP applications.
If catch_workers_output = yes is set in www.conf, PHP stderr (including errors) will be redirected to the PHP-FPM logs, which is very helpful for debugging.
6. Restart Services
After making any configuration changes, you must restart Nginx and PHP-FPM for the changes to take effect.
- Nginx:
nginx -t # Test Nginx configuration syntax nginx -s reload # Reload Nginx configuration (without dropping connections) # If a full restart is needed: # rc-service nginx restart # On Alpine with OpenRC # docker restart <nginx-container-id> # In Docker - PHP-FPM:
# On Alpine with OpenRC rc-service php-fpm restart # In Docker (if PHP-FPM is PID 1 or managed by supervisord) # docker restart <php-fpm-container-id-or-name> # or for supervisord inside container: # docker exec <php-fpm-container-id-or-name> supervisorctl restart php-fpmAlways test Nginx configuration with
nginx -tbefore reloading or restarting to avoid syntax errors that could take your web server offline.
7. Docker-Specific Considerations
If you're running Nginx and PHP-FPM in separate Docker containers, ensure the Unix socket is properly shared.
- Shared Volume for Socket:
The most robust way to share a Unix socket between containers is using a shared volume.
Ensure that the# docker-compose.yml example version: '3.8' services: nginx: image: nginx:alpine volumes: - ./nginx/conf.d:/etc/nginx/conf.d:ro - php-fpm-sock:/var/run/php # Mount the shared volume depends_on: - php-fpm # ... other nginx settings php-fpm: image: php:8.2-fpm-alpine # or similar volumes: - ./app:/var/www/html:rw - php-fpm-sock:/var/run/php # Mount the shared volume # ... other php-fpm settings volumes: php-fpm-sock: # Define the named volumelistendirective in PHP-FPM'swww.confandfastcgi_passin Nginx's config point to the same path within the respective containers that the shared volume is mounted to (e.g.,/var/run/php/php7.4-fpm.sock).
By systematically working through these steps, you should be able to identify and resolve the root cause of your Nginx 502 Bad Gateway error on Alpine Linux.
Multi-Platform Step-by-Step Implementation Matrix
The core underlying mechanism is identical across UNIX distributions, but configuration paths, daemon names, and kernel socket limits differ by platform. Follow the exact instructions for your target operating system below.
🐧 Debian 12 (Bookworm) & Ubuntu 24.04 / 22.04 LTS
On Debian-based systems, systemd service units and standard apt package paths apply:
# Verify active listening sockets and associated process IDs
sudo ss -tulpn | grep -E ":(80|443|3306|5432|6379)"
# Inspect live systemd daemon status
sudo systemctl status --no-pager nginx mysql postgresql redis
🔴 Rocky Linux 9, AlmaLinux & CentOS Stream
On RHEL-derived distributions, remember to check SELinux boolean contexts and firewalld rules:
# Check if SELinux is enforcing and denying network binds
sudo getenforce
sudo setsebool -P httpd_can_network_connect 1
# Open firewall ports
sudo firewall-cmd --permanent --add-service=http --add-service=https
sudo firewall-cmd --reload
🍎 macOS Local Environment (Homebrew)
On macOS, Homebrew services run in user space or launchd:
# Identify processes occupying ports on macOS
sudo lsof -iTCP -sTCP:LISTEN -n -P
# Restart Homebrew services
brew services restart nginx
🪟 Windows WSL2 (Ubuntu / Debian)
On WSL2, port forwarding bridges the Windows host with the WSL virtual adapter. Always ensure host port sharing is not blocked by Windows Fast Startup or native IIS/Hyper-V services:
# Check ports on Windows Host (PowerShell as Admin)
Get-NetTCPConnection -State Listen | Where-Object { $_.LocalPort -in 80,443,3306,5432 }
Our Production Verification Guarantee
Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.