502 Bad Gateway intermitente em Docker Compose com Nginx e PHP‑FPM: como diagnosticar e corrigir timeouts e excesso de conexões
Por Kevin Marcondes · Publicado em · 9 min de leitura
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 casoVeja também
- Como diagnosticar e capturar pacotes para intermitência de rede entre containers Docker no Debian
- Como reduzir tempo de build e evitar artefatos duplicados em CI para monólitos PHP grandes
- Como detectar e corrigir inconsistências de SKU duplicados entre WooCommerce e ERP sem perder pedidos
- Como reduzir tempo de recuperação após crash de servidor Linux com playbook Ansible e runbook de recuperação