Cómo resolver el error 'Too many open files' en Linux en servidores PHP‑FPM durante picos de carga

Por · Publicado el · 8 min de lectura

Notebook aberto sobre mesa com tela exibindo interface de edição, tablet gráfico e luminária ao fundo
Estação de trabalho limpa e profissional, adequada para guias e diagnóstico técnico Foto: Lalmch / Pixabay

Cómo resolver el error 'Too many open files' en Linux en servidores PHP‑FPM durante picos de carga: comprende qué agota los descriptores de archivo (FDs), cómo diagnosticar en producción y qué ajustes aplicar por capa (systemd, kernel, FPM y Nginx). A continuación verás pasos prácticos para estabilizar sin falsas promesas.

Síntomas reales y cuándo aparece el error

Ocurre en picos de tráfico, cuando PHP‑FPM acepta nuevas conexiones mientras mantiene FDs abiertos: sockets, logs, archivos temporales y conexiones a upstreams. Los síntomas incluyen 502/504 en Nginx, fallos intermitentes en APIs y mensajes de error en los logs del FPM y del servidor web.

No lo confundas con agotamiento de puertos efímeros. Aquí el problema es el techo de descriptores de archivo del proceso, del servicio o del kernel.

  • 502/504 y caídas momentáneas bajo carga.
  • Mensajes 'Too many open files' en los logs de PHP‑FPM.
  • Aumento de latencia y colas en el servidor web.
  • Conexiones a base de datos y caches fallando por falta de FDs.

Diagnóstico inmediato en producción (sin pánico)

Haz tres comprobaciones: 1) confirma el error en el log; 2) revisa el límite efectivo de FDs del master del FPM; 3) cuenta FDs abiertos para entender la brecha. Esto orienta el ajuste correcto por capa. Si ya expones métricas, correlaciona colas del FPM con el uso de FDs — por ejemplo, exponer métricas de PHP‑FPM.

# 1) Confirme no log do FPM
sudo grep -i "too many open files" /var/log/php*-fpm*.log || true

# 2) Descubra o PID do master do PHP-FPM
pgrep -x "php-fpm" || pgrep -x "php-fpm8.2" || pgrep -x "php-fpm7.4"
PID=<<coloque_o_pid_aqui>>

# Limites efetivos do processo (procfs)
cat /proc/$PID/limits | grep -i "open files"

# Também veja o LimitNOFILE do unit systemd correspondente
systemctl show php-fpm --property=LimitNOFILE 2>/dev/null || \
  systemctl show php8.2-fpm --property=LimitNOFILE 2>/dev/null

# 3) Quantos FDs o master mantém abertos agora?
lsof -p $PID | wc -l

# Tipos mais comuns de FDs (visão rápida)
lsof -p $PID | awk '{print $5}' | sort | uniq -c | sort -nr | head -n 10

De dónde viene el límite: capas que controlan los FDs

El techo de FDs es la combinación del kernel, el gestor de servicios y la aplicación. Ajustar solo el ulimit del shell no afecta a servicios gestionados por systemd. Entiende la pila antes de cambiar valores.

Capa Dónde ajustar Parámetro principal Cómo verificar
Kernel (global) /etc/sysctl.d/*.conf fs.file-max sysctl fs.file-max
systemd (servicio) override de la unit LimitNOFILE systemctl show <unit> --property=LimitNOFILE
PHP-FPM (pool) www.conf (o el pool correspondiente) rlimit_files /proc/$PID/limits (Open files)
Nginx (worker) nginx.conf worker_rlimit_nofile nginx -T | grep rlimit
Shell/PAM (login) /etc/security/limits*.conf nofile ulimit -n (en sesión interactiva)
Contenedores (Docker) compose.yaml / run ulimits: nofile docker inspect ... | jq '.[0].HostConfig.Ulimits'

Corrección rápida centrada en el servicio (systemd + FPM)

Aumenta el LimitNOFILE en la unit del PHP‑FPM y, opcionalmente, rlimit_files en el pool. Requiere reinicio del servicio, ya que los límites se aplican al crear el proceso. Para reducir impacto al usuario, considera una conmutación de tráfico con el patrón despliegue blue‑green antes del restart.

  • Usa un valor con margen para el pico previsto.
  • Revisa los logs tras el reinicio para confirmar.
  • No superes la capacidad del kernel (fs.file-max).
# 1) Override no systemd
sudo systemctl edit php-fpm || sudo systemctl edit php8.2-fpm
# Adicione no editor que abrir:
# [Service]
# LimitNOFILE=131072

sudo systemctl daemon-reload
sudo systemctl restart php-fpm || sudo systemctl restart php8.2-fpm

# 2) (Opcional) rlimit_files no pool do FPM
# Em /etc/php/*/fpm/pool.d/www.conf ou similar:
# rlimit_files = 131072
# Depois, reinicie o PHP-FPM novamente.

# Valide
systemctl show php-fpm --property=LimitNOFILE 2>/dev/null || \
  systemctl show php8.2-fpm --property=LimitNOFILE 2>/dev/null
cat /proc/$(pgrep -x php-fpm | head -n1)/limits | grep -i "open files"

Ajustes persistentes y dimensionamiento seguro

Asegura que el kernel y el servicio soporten el nuevo techo. Ajusta fs.file-max y revisa parámetros del pool del FPM para evitar un crecimiento descontrolado. Evita aumentar límites sin monitorizar CPU, memoria e I/O, porque puedes trasladar el cuello de botella.

  • Kernel: aumenta fs.file-max de forma persistente.
  • FPM: revisa pm.max_children y pm.process_idle_timeout.
  • Logs: una rotación adecuada evita exceso de FDs abiertos.
# Kernel: persistir um teto global mais alto
echo 'fs.file-max = 1000000' | sudo tee /etc/sysctl.d/99-fd.conf
sudo sysctl --system

# PHP-FPM (pool): além de rlimit_files, confira a capacidade de workers
# Ex.: pm = dynamic; pm.max_children = <valor_compatível_com_recursos>
# Consulte a documentação oficial do PHP-FPM para detalhes dos parâmetros.

Nginx, conexiones e interacción con contenedores (Docker)

Si Nginx hace proxy al FPM, los workers también necesitan un techo mayor. En contenedores, establece ulimits por servicio. Ten en cuenta que los límites del contenedor no heredan del host por defecto. Si estás decidiendo el proxy inverso, revisa criterios en Traefik vs Nginx.

  • Nginx: ajusta worker_rlimit_nofile y worker_connections.
  • Docker: define ulimits:nofile en el compose o al ejecutar containers.
  • Orquestadores aplican límites propios; revisa los manifests.
# docker-compose.yml (exemplo)
services:
  php-fpm:
    image: php:8.2-fpm
    ulimits:
      nofile:
        soft: 131072
        hard: 131072
  nginx:
    image: nginx:stable
    ulimits:
      nofile:
        soft: 131072
        hard: 131072

Prevención: métricas, logs y pruebas de carga

Estabiliza la operación con visibilidad y ensayos de pico. Mide FDs y colas del FPM, centraliza logs y prueba cambios antes del horario crítico. Combina alertas de FDs altos con gráficos de tráfico para anticipar la saturación.

Checklist de verificación post‑corrección

Confirma que los nuevos límites están activos y que la aplicación soporta el pico simulado. No ejecutes pruebas de carga en producción sin control. Usa ventana de mantenimiento, límites y monitorización en tiempo real.

  • LimitNOFILE de la unit está correcto.
  • /proc/$PID/limits refleja el nuevo techo.
  • lsof bajo carga permanece por debajo del límite.
  • Nginx y contenedores también ajustados.
  • Logs sin nuevos 'Too many open files'.
systemctl show php-fpm --property=LimitNOFILE 2>/dev/null || \
  systemctl show php8.2-fpm --property=LimitNOFILE 2>/dev/null
PID=$(pgrep -x php-fpm | head -n1)
cat /proc/$PID/limits | grep -i "open files"
# Simule carga leve e observe:
watch -n2 "lsof -p $PID | wc -l"

Preguntas frecuentes

¿Qué causa 'Too many open files' en PHP‑FPM?

El proceso alcanzó el techo de descriptores de archivo. Esto incluye sockets, archivos de log y temporales. En picos, cada worker mantiene múltiples FDs, que se suman a los límites del servicio (LimitNOFILE), del pool (rlimit_files) y del kernel (fs.file-max).

¿Es suficiente aumentar el ulimit en el shell para resolverlo?

No. Los servicios gestionados por systemd ignoran el ulimit de tu sesión interactiva. Debes ajustar LimitNOFILE en la unit del servicio y, opcionalmente, rlimit_files en el pool del PHP‑FPM, además de garantizar un fs.file-max adecuado en el kernel.

¿Necesito reiniciar PHP‑FPM después de cambiar los límites?

Sí. Límites como LimitNOFILE y rlimit_files se aplican al crear el proceso. Un reload no cambia esos valores. Planifica reiniciar el FPM de forma segura, drenando conexiones vía proxy inverso para evitar errores visibles al usuario.

¿Qué riesgos tiene definir límites demasiado altos?

Si fijas un límite muy alto sin capacidad real (CPU/RAM/I/O), el servidor puede aceptar más trabajo del que soporta y degradarse. Además, el kernel tiene un techo global (fs.file-max). Ajusta con margen razonable y monitoriza para calibrar.

¿Cómo ajustar en entornos con Docker Compose?

Define ulimits:nofile por servicio en el compose o al ejecutar containers. Ajusta Nginx y PHP‑FPM por separado. Ten en cuenta que los límites del host no se aplican automáticamente a los containers. Verifica con docker inspect y monitorea bajo carga.

¿Quieres ayuda para estabilizar tu PHP‑FPM en picos?

Diagnóstico guiado, ajustes de límites por capa y validaciones con observabilidad. Hablemos de tu escenario y de los pasos prácticos siguientes.

Hablar sobre mi entorno

Ver también