
El dashboard clásico tiene tres medidores: CPU, memoria, disco. Cuando un servidor se porta mal, ahí es donde mira todo el mundo primero. Por eso la peor caída es la que tiene los tres en verde mientras el sitio sigue descartando conexiones, devolviendo 500 o rechazando logins al azar.
Lo que pasa es real, solo que no está en ese dashboard. Un puñado de límites del kernel y por proceso se agotan con independencia de la CPU, la RAM y el espacio en disco — y cuando lo hacen, el síntoma aparece dos capas más allá de la causa. Estos son los tres que más atrapan a una flota, cómo confirmar cada uno en un comando, y por qué ninguno es visible en los medidores que estabas mirando.
1. La tabla de conexiones está llena
El primero casi nadie lo monitorea. El kernel registra cada conexión en una tabla (nf_conntrack), y esa tabla tiene un tamaño fijo. En un servidor con tráfico detrás de un firewall — o sea, cualquier servidor — se llena. Cuando ocurre, el kernel deja de aceptar conexiones nuevas y escribe una línea en el log:
nf_conntrack: table full, dropping packet
Desde fuera parece una locura: el servidor está arriba, la CPU al 20%, pero a los visitantes nuevos se les agota el tiempo mientras las conexiones existentes van bien. Comprueba lo cerca que estás:
# conexiones rastreadas actuales vs máximo
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
Si count anda cerca de max, estás a un pico de tráfico de empezar a tirar paquetes. El arreglo es subir net.netfilter.nf_conntrack_max (y el hashsize) en sysctl, o acortar los timeouts que mantienen conexiones muertas en la tabla — pero el punto de este artículo está aguas arriba del arreglo: nada en un dashboard de CPU/RAM/disco te habría avisado de que esto venía.
2. Sin descriptores de archivo
Cada archivo abierto, cada socket, cada pipe cuesta un descriptor de archivo. Hay un techo por proceso y otro a nivel de sistema, y los servicios con carga — una base de datos, un pool de FPM, un proxy orientado a eventos — los consumen rápido. El síntoma a nivel de aplicación es inconfundible una vez que lo conoces:
accept4(): Too many open files
Dos límites que revisar, porque cualquiera puede ser el que toques:
# a nivel de sistema: asignados / sin usar / máximo
cat /proc/sys/fs/file-nr
# techo por proceso de un servicio en ejecución (por PID)
cat /proc/<pid>/limits | grep "open files"
El de por proceso es el que más muerde, porque suele ser un default rancio. Un servicio de systemd hereda LimitNOFILE, y el valor que hace tres años sobraba hoy estrangula al mismo servicio. El disco no está lleno, la memoria va bien — el proceso simplemente no tiene permiso de abrir otro socket. Sube LimitNOFILE en la unit (o fs.file-max a nivel de sistema) y recarga.
3. Sin inodos (con disco de sobra)
Este produce el error más confuso de todos: No space left on device mientras df -h jura que el disco está medio vacío. Al sistema de archivos se le acabaron los inodos, no los bytes. Cada archivo consume un inodo sin importar su tamaño, así que unos millones de archivos minúsculos — archivos de sesión de PHP, fragmentos de caché, un maildir, un directorio de logs sin rotar — agotan la tabla de inodos sin apenas tocar la capacidad.
df -h no lo muestra. El flag -i sí:
df -i
# mira IUse% — puede marcar 100% mientras df -h muestra 50%
Una vez que sabes dónde mirar, el arreglo suele ser de limpieza (encuentra el directorio con millones de archivos y vacíalo, o corrige lo que los sigue creando). La trampa es la media tarde que puedes perder antes de saber dónde mirar, persiguiendo un “disco lleno” en un disco que no lo está.
El patrón, no los tres errores
Conntrack, descriptores de archivo e inodos son solo los tres más comunes. La lista sigue — agotamiento de puertos efímeros con mucho fan-out saliente, el límite de PIDs por cgroup (pids.max) topando cuántas tareas puede lanzar un servicio, sockets en TIME_WAIT acumulándose. Comparten una forma:
- Son límites duros, no degradación gradual. Hasta el 99% todo va perfecto; al 100% se cae por el precipicio. No hay una curva de calentamiento que notar.
- El síntoma está dislocado de la causa. “A los visitantes nuevos se les agota el tiempo” no dice tabla de conexiones. “Too many open files” en un servicio no dice que el límite es un default de
systemd. “No space left” miente activamente sobre qué está lleno. - Ninguno es CPU, RAM ni disco. Los tres medidores que todos miran estarán en verde durante cada una de estas caídas.
Por eso mismo se comen horas: el dashboard dice “sano”, así que desconfías del dashboard antes que de un límite que ni sabías que tenías.
Vigilar los límites, no solo los medidores
El arreglo a nivel de flota no es memorizar estos comandos — es tratar los límites como métricas de primera clase, junto a la CPU y la memoria, para enterarte al 80% en vez de en la caída. El agente de CentralHost muestrea la utilización de conntrack, la presión de descriptores de archivo y el uso de inodos en cada host gestionado, y alerta por flanco (edge-triggered) cuando uno cruza su umbral — la alerta salta una vez al subir, no cada minuto mientras sigue caliente.
Y cuando algo se rompe, el AI Assistant está hecho para razonar justo sobre esa dislocación. Le pasas el síntoma — “a este servidor las conexiones nuevas se le caen de forma intermitente” — y correlaciona los drops de red con el contador de conntrack, en vez de empezar, como haría un humano, mirando fijamente una gráfica de CPU que no tiene nada que decirle. La causa está dos capas por debajo del error; la idea es no pasarte la tarde descubriendo cuáles son esas dos.
La próxima vez que un servidor “no tenga nada” y aun así falle, el primer movimiento no es la gráfica de CPU. Es el límite que nadie puso en el dashboard.