Troubleshooting Nginx 502 Bad Gateway with PHP-FPM Unix Socket on Alpine Linux

Fix Nginx 502 Bad Gateway errors on Alpine Linux when using PHP-FPM via a Unix socket. Diagnose common issues like permissions, socket path, and PHP-FPM configuration.


Fix Nginx 502 Bad Gateway errors on Alpine Linux when using PHP-FPM via a Unix socket. Diagnose common issues like permissions, socket path, and PHP-FPM configuration.

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.

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:

  1. 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.
  2. 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).
  3. 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 the nginx user by default for Nginx, and www-data or a similar user for PHP-FPM.
  4. PHP-FPM Pool Configuration Issues: The listen.owner, listen.group, and listen.mode directives in the PHP-FPM pool configuration file (www.conf or similar) are crucial for setting correct socket permissions.
  5. 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".
  6. 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.
  7. 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-fpm is not listed in docker 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 start
    
  • General process check (works everywhere):

    ps aux | grep php-fpm
    

    You should see several php-fpm: pool www processes 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.log or 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.conf or /etc/nginx/http.d/default.conf). Find the fastcgi_pass directive:
    # /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.conf on Alpine, replace php8 with your PHP version). Find the listen directive:
    ; /etc/php8/php-fpm.d/www.conf
    [www]
    listen = /var/run/php/php7.4-fpm.sock ; <-- This path must match Nginx
    

    Ensure 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 the nginx user, would need to be part of the www-data group or have appropriate listen.mode settings.

  • Check Nginx user: Inspect your main Nginx configuration file (/etc/nginx/nginx.conf) for the user directive:

    # /etc/nginx/nginx.conf
    user nginx; # <-- Nginx runs as this user
    worker_processes auto;
    # ...
    
  • Adjust PHP-FPM listen directives: In your PHP-FPM pool configuration (/etc/php8/php-fpm.d/www.conf), modify the listen.owner, listen.group, and listen.mode directives to grant Nginx access.

    Option A: Add Nginx user to PHP-FPM group (Recommended): Ensure the nginx user 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-data
    

    Then, 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/write
    

    Option 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.conf or site-specific conf): Ensure fastcgi_buffer_size, fastcgi_buffers, and fastcgi_read_timeout are 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 = 35
    

    Also, check php.ini (/etc/php8/php.ini) for memory_limit and max_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-fpm
    

    Always test Nginx configuration with nginx -t before 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.
    # 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 volume
    
    Ensure that the listen directive in PHP-FPM's www.conf and fastcgi_pass in 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.