502 Bad Gateway intermitente em Docker Compose com Nginx e PHP‑FPM: como diagnosticar e corrigir timeouts e excesso de conexões

Por · Publicado em · 9 min de leitura

Pilha de troncos cortados empilhados ao ar livre com foco nas faces cortadas
Textura e repetição que funcionam bem como imagem de destaque em cabeçalhos Foto: Lipot Repaszky / Pexels

502 Bad Gateway intermitente em Docker Compose com Nginx e PHP‑FPM: como diagnosticar e corrigir timeouts e excesso de conexões. O 502 ocorre quando o Nginx falha ao falar com o PHP‑FPM ou quando o backend fecha a conexão cedo. Veja checagens rápidas, ajustes de timeouts, FPM e limites do sistema para estabilizar a stack sem mascarar lentidão.

Diagnóstico rápido: o que verificar primeiro

Confirme onde o 502 acontece e colete evidências. Cruze erros do Nginx com sinais de saturação no PHP‑FPM e limites do sistema. Reproduza com um endpoint leve e outro pesado para comparar.

Se o 502 Bad Gateway intermitente em Docker Compose com Nginx e PHP‑FPM só aparece sob pico, verifique reinicializações de containers e a rede da bridge. Coletar logs próximos ao evento acelera a causa raiz.

  • Logs de erro do Nginx: mensagens com upstream, connect() e timed out.
  • Logs do PHP‑FPM: slowlog, max_children atingido, workers travados.
  • Uso de CPU/RAM e contagem de processos do FPM durante o pico.
  • Reinícios de containers próximo ao incidente.
# 1) Estado rápido dos serviços
docker compose ps

# 2) Últimos erros do Nginx (procure upstream/timeout)
docker compose logs -f nginx | sed -n '1,200p'
# Opcional: dentro do container, filtrar erros comuns
# docker compose exec nginx sh -lc 'grep -E "upstream|connect\(\)|timed out" -n /var/log/nginx/error.log | tail -n 50'

# 3) Erros do PHP-FPM (stdout/err costuma concentrar)
docker compose logs -f php-fpm | sed -n '1,200p'

# 4) Teste simples de latência/HTTP code (ajuste URL)
curl -s -o /dev/null -w "%{http_code} %{time_total}\n" https://seu-dominio/health

O que causa 502 Bad Gateway intermitente nessa stack?

As causas mais comuns misturam timeout e saturação. O Nginx retorna 502 quando não consegue se comunicar com o upstream (PHP‑FPM) ou recebe fechamento prematuro da conexão. A tabela abaixo mapeia sintomas para a provável causa e onde ajustar.

Quedas de rede e DNS em Docker também geram intermitência. Se a bridge perde pacotes, o Nginx pode não conectar no FPM. Veja também por que containers perdem conexão em produção: conexão na Docker bridge.

Sintoma no log Provável causa Onde ajustar
upstream timed out (110) while reading response header Resposta lenta; timeout baixo Nginx: fastcgi_read_timeout; App: otimização do endpoint
connect() failed (111: Connection refused) to upstream FPM caiu/reiniciou; backlog estourado FPM: listen.backlog; Docker: saúde/restart; sistema: somaxconn
upstream prematurely closed connection Worker do FPM matou requisição FPM: request_terminate_timeout; app: timeouts internos
no such file or directory while connecting to upstream Socket Unix inexistente/permissão FPM: listen=; permissões; volume do socket
Too many open files Limite de descritores pequeno ulimit nofile; Nginx/FPM worker_rlimit_nofile

Como diagnosticar timeouts entre Nginx e PHP‑FPM?

Leia o error.log do Nginx e aumente os timeouts com critério. Se o 502 some ao elevar fastcgi_read_timeout, a causa é processamento lento ou bloqueios no backend. Aumentar timeout mascara lentidão; corrija a causa raiz depois.

Ajuste também fastcgi_connect_timeout e mantenha a conexão com fastcgi_keep_conn on para reduzir overhead de handshakes.

  • Procure por upstream timed out, prematurely closed connection e connect() failed.
  • Teste um endpoint leve (/health) e um pesado para comparar.
  • Evite timeouts desbalanceados entre Nginx e FPM.
# Exemplo de server com timeouts explícitos
server {
  listen 80;
  server_name exemplo.local;

  location ~ \.php$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    # Upstream para o FPM no Compose (serviço "php-fpm")
    fastcgi_pass php-fpm:9000; # ou unix:/var/run/php/php-fpm.sock

    # Timeouts: comece conservador e valide com carga
    fastcgi_connect_timeout 3s;
    fastcgi_send_timeout 30s;
    fastcgi_read_timeout 30s;
    fastcgi_keep_conn on;
  }
}

Quantas conexões o PHP‑FPM aguenta de verdade?

O limite efetivo é pm.max_children. Cada child consome memória; calcule com base no maior pico. pm.max_requests ajuda a reciclar workers que crescem demais. Aumentar pm.max_children sem memória disponível causa OOM e reinícios.

Consulte a documentação do FPM para detalhes de pm.* e timeouts. Para dimensionamento avançado, veja o nosso guia de tuning de FPM: tuning para picos sazonais.

  • Meça RSS por worker: pico de memória do child multiplicado por max_children.
  • Prefira pm=dynamic para tráfego variável; ondemand em ambientes ociosos.
  • Ative status e slowlog para evidenciar gargalos.
; pool.d/www.conf — exemplo base (ajuste ao seu tráfego)
pm = dynamic
pm.max_children = 24
pm.start_servers = 6
pm.min_spare_servers = 4
pm.max_spare_servers = 12
pm.max_requests = 500

; Tempo-limite duro por request (protege contra travas)
request_terminate_timeout = 30s

; Backlog do socket (fila de conexões pendentes)
listen.backlog = 1024

; Observabilidade
pm.status_path = /fpm-status
slowlog = /var/log/php-fpm/slow.log
request_slowlog_timeout = 5s

Quando usar socket Unix vs TCP para o upstream?

Sockets Unix reduzem overhead e latência no mesmo host, mas exigem volume compartilhado e permissões corretas. TCP é mais simples entre containers e facilita debug com ss e tcpdump.

Em Compose no mesmo host, sockets costumam performar melhor. Em múltiplos hosts, use TCP.

  • Unix: menor latência; atenção a listen.backlog e permissões UID/GID.
  • TCP: debug simples; respeite MTU e verifique a bridge do Docker.
  • Não exponha 9000 publicamente; rede só interna do Compose.
# docker-compose.yml — exemplo com socket Unix
services:
  php-fpm:
    image: php:fpm
    volumes:
      - php-sock:/var/run/php
      - ./:/var/www/html
  nginx:
    image: nginx:alpine
    volumes:
      - php-sock:/var/run/php
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - php-fpm
volumes:
  php-sock:

# www.conf (FPM)
; listen para socket Unix compartilhado
listen = /var/run/php/php-fpm.sock
listen.mode = 0660
listen.group = www-data

# nginx.conf (trecho)
location ~ \.php$ {
  include fastcgi_params;
  fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  fastcgi_pass unix:/var/run/php/php-fpm.sock;
  fastcgi_read_timeout 30s;
}

Excesso de conexões e erros de connect(): como checar?

Erros de connect() apontam para FPM indisponível, backlog estourado ou esgotamento de descritores. Valide fila do socket, somaxconn e nofile. Limites muito baixos geram 502 sob picos.

Ajuste ulimits no Compose e verifique se o kernel aceita filas maiores que seu listen.backlog.

  • Compare listen.backlog com net.core.somaxconn.
  • Cheque nofile para Nginx e FPM.
  • Monitore conexões ativas no :9000 ou no socket.
# Conexões/escuta do FPM (TCP)
ss -ltnp | grep :9000 || true

# Backlog máximo do kernel (precisa ser >= listen.backlog)
cat /proc/sys/net/core/somaxconn

# Limite de arquivos por processo (dentro do container)
ulimit -n

# Procure indicadores no config do FPM
# docker compose exec php-fpm sh -lc "grep -E 'pm\.max_children|listen\.backlog' -n /usr/local/etc/php-fpm.d/*.conf"

# Se ver 'Too many open files', ajuste também:
# - ulimits no Compose para Nginx e FPM
# - worker_rlimit_nofile (Nginx)
# Guia correlato: [resolver 'Too many open files'](/too-many-open-files-linux-php-fpm)

Health checks e limites no Docker Compose para evitar quedas

Healthchecks reduzem janelas de 502 expondo apenas instâncias saudáveis. Combine com restart policies e stop_grace_period para encerrar workers com dignidade. Ajuste ulimits para suportar picos de conexões.

Para readiness da aplicação, aponte o healthcheck para um endpoint leve. Em stacks PHP, publicar /health ajuda a validar dependências externas.

  • Healthcheck simples no php-fpm ou no Nginx.
  • stop_grace_period suficiente para requests longas.
  • ulimits nofile coerente com tráfego.
services:
  php-fpm:
    image: php:fpm
    healthcheck:
      test: ["CMD-SHELL", "php-fpm -t >/dev/null 2>&1 || exit 1"]
      interval: 10s
      timeout: 3s
      retries: 3
    ulimits:
      nofile:
        soft: 65535
        hard: 65535
    stop_grace_period: 30s

  nginx:
    image: nginx:alpine
    depends_on:
      - php-fpm
    ulimits:
      nofile:
        soft: 65535
        hard: 65535
    restart: unless-stopped

Checklist de correções na ordem certa

Priorize validação de causa antes de aumentar limites. A sequência abaixo evita ajustes às cegas e reduz risco de mascarar problemas de aplicação.

Para métricas operacionais e alertas do FPM em Compose, siga o tutorial de métricas para Prometheus: expor métricas do PHP‑FPM.

  • 1) Reproduza o 502 e colete logs Nginx/FPM próximos ao evento.
  • 2) Confirme se é timeout: ajuste fastcgi_connect/read_timeout de forma incremental.
  • 3) Verifique pm.max_children, slowlog e memória por worker; ajuste pool.
  • 4) Corrija nofile, listen.backlog e somaxconn se houver filas/erros de connect().
  • 5) Otimize endpoints lentos; só então mantenha timeouts maiores.
  • 6) Adicione healthchecks e restart graceful para reduzir janelas de falha.
  • 7) Revalide com teste de carga controlado e monitore. Em 502 Bad Gateway intermitente em Docker Compose com Nginx e PHP‑FPM, medir e ajustar com dados evita recidivas.

Perguntas frequentes

Como saber se o 502 vem de timeout ou de queda do PHP‑FPM?

Leia o error.log do Nginx: upstream timed out indica timeout; connect() failed/refused indica queda do FPM ou backlog cheio. No FPM, veja max_children atingido, OOM e reinícios. Se aumentar fastcgi_read_timeout “resolve” temporariamente, a causa é lentidão no backend.

Qual timeout ajustar primeiro para reduzir 502?

Ajuste fastcgi_connect_timeout para conexões rápidas (2–5s) e fastcgi_read_timeout de forma incremental (20–30s) enquanto corrige endpoints lentos. Evite valores excessivos, pois mascaram problemas e aumentam filas no Nginx e no FPM.

Aumentar pm.max_children sempre resolve 502 Bad Gateway?

Não. Aumentar pm.max_children sem memória suficiente causa OOM e reinícios, piorando 502. Antes, meça o consumo por worker e ajuste pm.max_requests. Otimize endpoints com slowlog. Só aumente max_children quando houver capacidade real.

Como ver a fila do PHP‑FPM e processos em tempo real?

Ative pm.status_path e consulte /fpm-status para ver processos e estados. Monitore conexões com ss/netstat e a fila do socket (listen.backlog vs somaxconn). Métricas no Prometheus ajudam a detectar saturação antecipada.

Vale migrar de TCP para socket Unix para ganhar estabilidade?

Em um único host, sockets Unix reduzem overhead e podem estabilizar latências. Em múltiplos hosts ou quando precisa de observabilidade de rede, TCP facilita. Teste ambos sob carga, mantendo listen.backlog e permissões corretos.

Healthchecks no Compose evitam 502 durante deploy?

Sim. Healthchecks expõem apenas instâncias saudáveis e, com stop_grace_period adequado, evitam encerrar requisições em andamento. Combine com limites nofile e monitoramento para reduzir janelas de 502 durante trocas de versão.

Quer estabilizar Nginx + PHP‑FPM no Docker Compose?

Se você enfrenta 502 intermitente, podemos revisar logs, parâmetros críticos e arquitetura para chegar à causa raiz e aplicar correções seguras.

Falar sobre meu caso

Veja também