502 Bad Gateway intermitente en Docker Compose con Nginx y PHP‑FPM: cómo diagnosticar y corregir timeouts y exceso de conexiones
Por Kevin Marcondes · Publicado el · 9 min de lectura
502 Bad Gateway intermitente en Docker Compose con Nginx y PHP‑FPM: cómo diagnosticar y corregir timeouts y exceso de conexiones. El 502 aparece cuando Nginx no logra comunicarse con PHP‑FPM o cuando el backend cierra la conexión antes de tiempo. Revisa comprobaciones rápidas, ajustes de timeouts, parámetros de FPM y límites del sistema para estabilizar la pila sin enmascarar latencia.
Diagnóstico rápido: qué verificar primero
Confirma dónde ocurre el 502 y recopila evidencias. Cruza errores de Nginx con señales de saturación en PHP‑FPM y límites del sistema. Reproduce el fallo con un endpoint ligero y otro pesado para comparar.
Si el 502 Bad Gateway intermitente en Docker Compose con Nginx y PHP‑FPM solo aparece bajo pico, revisa reinicios de contenedores y la red bridge. Recolectar logs cercanos al evento acelera la identificación de la causa raíz.
- Logs de error de Nginx: mensajes con upstream, connect() y timed out.
- Logs de PHP‑FPM: slowlog, max_children alcanzado, workers bloqueados.
- Uso de CPU/RAM y conteo de procesos del FPM durante el pico.
- Reinicios de contenedores cerca del 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
¿Qué causa un 502 Bad Gateway intermitente en esta pila?
Las causas más comunes combinan timeout y saturación. Nginx devuelve 502 cuando no puede comunicarse con el upstream (PHP‑FPM) o recibe un cierre prematuro de la conexión. La tabla siguiente mapea síntomas a la probable causa y dónde ajustar.
Caídas de red y DNS en Docker también generan intermitencia. Si el bridge pierde paquetes, Nginx puede no conectar con el FPM. Mira también por qué los contenedores pierden conexión en producción: conexiones en el bridge de Docker.
| Síntoma en el log | Causa probable | Dónde ajustar |
|---|---|---|
| upstream timed out (110) while reading response header | Respuesta lenta; timeout bajo | Nginx: fastcgi_read_timeout; App: optimización del endpoint |
| connect() failed (111: Connection refused) to upstream | FPM cayó/reinició; backlog saturado | FPM: listen.backlog; Docker: health/restart; sistema: somaxconn |
| upstream prematurely closed connection | Worker del FPM mató la petición | FPM: request_terminate_timeout; app: timeouts internos |
| no such file or directory while connecting to upstream | Socket Unix inexistente/permiso | FPM: listen=; permisos; volumen del socket |
| Too many open files | Límite de descriptores bajo | ulimit nofile; Nginx/FPM worker_rlimit_nofile |
Cómo diagnosticar timeouts entre Nginx y PHP‑FPM
Lee el error.log de Nginx y aumenta los timeouts con criterio. Si el 502 desaparece al elevar fastcgi_read_timeout, la causa es procesamiento lento o bloqueos en el backend. Aumentar el timeout enmascara la lentitud; corrige la causa raíz después.
Ajusta también fastcgi_connect_timeout y mantén la conexión con fastcgi_keep_conn on para reducir el overhead de handshakes.
- Busca upstream timed out, prematurely closed connection y connect() failed.
- Prueba un endpoint ligero (/health) y uno pesado para comparar.
- Evita timeouts desbalanceados entre Nginx y 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;
}
}
¿Cuántas conexiones aguanta realmente PHP‑FPM?
El límite efectivo es pm.max_children. Cada child consume memoria; calcula en base al pico máximo. pm.max_requests ayuda a reciclar workers que crecen demasiado. Aumentar pm.max_children sin memoria disponible provoca OOM y reinicios.
Consulta la documentación de FPM para detalles de pm.* y timeouts. Para dimensionamiento avanzado, mira nuestra guía de tuning de FPM: dimensionamiento y tuning para picos.
- Mide el RSS por worker: pico de memoria del child multiplicado por max_children.
- Prefiere pm=dynamic para tráfico variable; ondemand en entornos poco cargados.
- Activa status y slowlog para evidenciar cuellos de botella.
; 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
Cuándo usar socket Unix vs TCP para el upstream
Los sockets Unix reducen overhead y latencia en el mismo host, pero requieren volumen compartido y permisos correctos. TCP es más simple entre contenedores y facilita el debug con ss y tcpdump.
En Compose en el mismo host, los sockets suelen rendir mejor. En múltiples hosts, usa TCP.
- Unix: menor latencia; atención a listen.backlog y permisos UID/GID.
- TCP: debug sencillo; respeta MTU y revisa la bridge de Docker.
- No expongas el puerto 9000 públicamente; usa la red interna del 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;
}
Exceso de conexiones y errores de connect(): cómo comprobar
Los errores de connect() apuntan a FPM indisponible, backlog saturado o agotamiento de descriptores. Valida la cola del socket, somaxconn y nofile. Límites demasiado bajos generan 502 bajo picos.
Ajusta ulimits en Compose y verifica que el kernel acepte colas mayores que tu listen.backlog.
- Compara listen.backlog con net.core.somaxconn.
- Revisa nofile para Nginx y FPM.
- Monitorea conexiones activas en :9000 o en el 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'](/es/too-many-open-files-php-fpm)
Healthchecks y límites en Docker Compose para evitar caídas
Los healthchecks reducen ventanas de 502 exponiendo solo instancias saludables. Combínalos con políticas de reinicio y stop_grace_period para terminar workers con dignidad. Ajusta ulimits para soportar picos de conexiones.
Para readiness de la aplicación, apunta el healthcheck a un endpoint ligero. En stacks PHP, publicar /health ayuda a validar dependencias externas.
- Healthcheck simple en php-fpm o en Nginx.
- stop_grace_period suficiente para requests largas.
- ulimits nofile coherente con el tráfico.
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 correcciones en el orden correcto
Prioriza la validación de la causa antes de aumentar límites. La secuencia abajo evita ajustes a ciegas y reduce el riesgo de enmascarar problemas de aplicación.
Para métricas operativas y alertas del FPM en Compose, sigue el tutorial para exponer métricas: exponer métricas del PHP‑FPM.
- 1) Reproduce el 502 y recopila logs Nginx/FPM cercanos al evento.
- 2) Confirma si es timeout: ajusta fastcgi_connect/read_timeout de forma incremental.
- 3) Verifica pm.max_children, slowlog y memoria por worker; ajusta el pool.
- 4) Corrige nofile, listen.backlog y somaxconn si hay colas/errores de connect().
- 5) Optimiza endpoints lentos; solo entonces mantén timeouts mayores.
- 6) Añade healthchecks y restart graceful para reducir ventanas de fallo.
- 7) Revalida con pruebas de carga controladas y monitoriza. En 502 Bad Gateway intermitente en Docker Compose con Nginx y PHP‑FPM, medir y ajustar con datos evita recaídas.
Preguntas frecuentes
Cómo saber si el 502 viene de timeout o de caída del PHP‑FPM?
Lee el error.log de Nginx: upstream timed out indica timeout; connect() failed/refused indica caída del FPM o backlog lleno. En el FPM, revisa max_children alcanzado, OOM y reinicios. Si aumentar fastcgi_read_timeout “resuelve” temporalmente, la causa es lentitud del backend.
Qué timeout ajustar primero para reducir 502?
Ajusta fastcgi_connect_timeout para conexiones rápidas (2–5s) y fastcgi_read_timeout de forma incremental (20–30s) mientras corriges endpoints lentos. Evita valores excesivos, porque enmascaran problemas y aumentan las colas en Nginx y FPM.
Aumentar pm.max_children siempre resuelve 502 Bad Gateway?
No. Aumentar pm.max_children sin memoria suficiente provoca OOM y reinicios, empeorando el 502. Antes, mide el consumo por worker y ajusta pm.max_requests. Optimiza endpoints con slowlog. Solo aumenta max_children si hay capacidad real.
Cómo ver la cola del PHP‑FPM y procesos en tiempo real?
Activa pm.status_path y consulta /fpm-status para ver procesos y estados. Monitorea conexiones con ss/netstat y la cola del socket (listen.backlog vs somaxconn). Las métricas en Prometheus ayudan a detectar saturación anticipada.
Vale la pena migrar de TCP a socket Unix para ganar estabilidad?
En un único host, los sockets Unix reducen overhead y pueden estabilizar latencias. En múltiples hosts o cuando necesitas observabilidad de red, TCP facilita las herramientas de debug. Prueba ambos bajo carga, manteniendo listen.backlog y permisos correctos.
Los healthchecks en Compose evitan 502 durante deploy?
Sí. Los healthchecks exponen solo instancias saludables y, con stop_grace_period adecuado, evitan terminar peticiones en curso. Combínalos con límites nofile y monitorización para reducir ventanas de 502 durante cambios de versión.
¿Quieres estabilizar Nginx + PHP‑FPM en Docker Compose?
Si sufres 502 intermitente, podemos revisar logs, parámetros críticos y arquitectura para encontrar la causa raíz y aplicar correcciones seguras.
Hablar sobre mi casoVer también
- Cómo diagnosticar y capturar paquetes por intermitencias de red entre contenedores Docker en Debian
- Cómo detectar y corregir inconsistencias de SKUs duplicados entre WooCommerce y ERP sin perder pedidos
- Cómo resolver el error 'Too many open files' en Linux en servidores PHP‑FPM durante picos de carga
- Cómo reducir el tiempo de recuperación tras un crash de un servidor Linux con playbook Ansible y runbook de recuperación