PHP-FPM and Nginx in Docker: Fixing Random 502 Errors

Running Nginx and PHP-FPM in separate Docker containers is common, but misconfiguration can produce annoying random 502 errors.

Typical symptoms

  • Nginx error log shows upstream prematurely closed connection.
  • 502s appear under load or when logging a lot.
  • Containers look healthy, but the app responds with 502 sporadically.

We’ll look at the usual suspects: Nginx buffers, PHP-FPM logging, and FPM pool limits.

Example docker-compose setup

docker-compose.yml:

YAML
services:
  nginx:
    image: nginx:1.27-alpine
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./:/var/www/html
    ports:
      - "80:80"
    depends_on:
      - php

  php:
    image: php:8.3-fpm-alpine
    volumes:
      - ./:/var/www/html

Nginx config nginx.conf:

Nginx
server {
    listen 80;
    server_name _;

    root /var/www/html/public;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
    }
}

Fix 1: Tune Nginx buffer sizes

If your app returns large headers or cookies, default buffers are too small. Add:

Nginx
http {
    sendfile on;

    fastcgi_buffers 16 16k;
    fastcgi_buffer_size 32k;
    fastcgi_busy_buffers_size 64k;

    client_max_body_size 10m;
}

In a single‑file config, wrap your server block in an http block with these settings.

Fix 2: PHP-FPM log_limit and stderr

Older Docker PHP images used a non‑default log_limit which, combined with FPM stderr, triggered issues with Nginx.

In your PHP-FPM config (e.g. /usr/local/etc/php-fpm.d/zz-docker.conf):

INI
[global]
log_limit = 1024
error_log = /proc/self/fd/2

[www]
catch_workers_output = yes

If you see 502s correlated with bursts of PHP notices/warnings, lowering log_limit back to defaults (or at least not setting it excessively high) avoids FPM trying to push huge log chunks over stderr.

Fix 3: FPM pool max_children and timeouts

Open www.conf in the container:

INI
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 4

request_terminate_timeout = 60

Then rebuild your PHP image with that config.

If pm.max_children is too low, FPM can’t keep up; if it’s too high, memory exhaustion kills workers and causes 502s.

Fix 4: Health checks and slowlog

Add a health endpoint to check PHP-FPM responsiveness:

PHP
// public/health.php

http_response_code(200);
echo 'OK';

In Nginx:

Nginx
location = /health {
    fastcgi_pass php:9000;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
}

Configure an FPM slowly:

INI
[www]
request_slowlog_timeout = 5s
slowlog = /proc/self/fd/2

Slowlog entries help you find code paths that consistently break under load.

Common mistakes and fixes

MistakeFix
Mounting code into containers but forgetting to reload Nginx after config changes.Always docker compose restart nginx after editing Nginx config.
Leaving PHP error reporting at development levels in production.Reduce noise: log errors, but avoid floods of notices that stress FPM.
Assuming all 502s are due to Nginx.Tail both logs (docker logs phpdocker logs nginx) and correlate timestamps; often the root cause is PHP crashing or timing out.

Tuning these three areas removes the majority of “random” 502s in containerized PHP stacks.


Posted

in

, , ,

by

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *