Un disco lleno no avisa a nadie
El escenario se repite. Un VPS de cliente funciona desde hace meses, la base de datos escribe sus registros, las copias de seguridad se acumulan y una mañana el servicio se detiene. No salió ninguna alerta y el cliente se entera antes que usted. En PostgreSQL, un caso comunicado en la lista oficial del proyecto muestra la línea exacta: PANIC: could not write to file "pg_wal/xlogtemp.11399": No space left on device. Un PANIC no es una simple advertencia: el servidor ya no puede registrar sus escrituras, se detiene y solo reinicia correctamente cuando se libera espacio.
La dificultad no es detectar un disco lleno, sino detectarlo antes. Entre el 80 % de ocupación y la caída suelen quedar horas o días, y esa es justo la ventana que debe captar una alerta bien ajustada. Esta guía no repite la instalación de una pila de supervisión: coloca lo mínimo útil en cada VPS, en shell, sin un segundo servidor que mantener.
Las seis alertas mínimas y el latido, en cada VPS
- Disco — dos umbrales, 80 % como aviso y 90 % como crítico, en cada punto de montaje relevante.
- Inodos — un disco puede rechazar escrituras aunque queden bytes libres, si los inodos están agotados.
- Memoria y OOM — detectar que el kernel mató un proceso, y no solo que la RAM está alta.
- Servicios systemd — recibir un aviso cuando la base de datos, el servidor web o el proxy pasa al estado failed.
- Certificados — avisar 21 días antes del vencimiento, cuando la renovación automática ha fallado en silencio.
- Copias de seguridad — avisar por la ausencia de un éxito reciente, no solo por un error explícito.
- El propio control — un latido externo que avisa cuando dejan de enviarse las alertas.
Requisitos en cifras: casi nada
Todo lo que sigue cabe en unas decenas de líneas de shell POSIX. Hace falta un VPS Linux con curl, cron o los timers de systemd, acceso root para escribir en /usr/local/bin y /etc, y alrededor de 1 MB de disco. El coste en memoria es el de un script que dura un segundo cada cinco minutos: despreciable, incluso en el VPS más pequeño.
Dos elementos externos completan el dispositivo: un canal de recepción (un bot de Telegram, o cualquier webhook accesible por HTTP) y un servicio de latido, que recibe un ping regular y le avisa cuando se detiene. Para este segundo punto, Healthchecks.io ofrece un plan gratuito limitado a 20 jobs y 100 entradas de registro por job, lo que cubre de sobra un parque pequeño; compruebe esos límites en la fuente antes de fiarse. Si no quiere depender de ningún servicio externo, el modo push de Uptime Kuma cumple el mismo papel en una máquina propia.
Disco e inodos: los umbrales y lo que los desplaza
El comando útil es df -h --output=pcent,target: solo muestra el porcentaje y el punto de montaje, que se lee y se procesa con facilidad. Para los inodos, la misma idea se convierte en df --output=ipcent,target. Una carpeta de sesiones o de pequeños archivos de caché puede consumir todos los inodos de un sistema de archivos ext4 mientras df -h aún muestra un 40 % de ocupación: la escritura falla igualmente con No space left on device.
Hay una segunda sutileza. En ext4, el 5 % de los bloques está reservado por defecto a los procesos privilegiados, según la página de manual de tune2fs; así los demonios del sistema pueden seguir escribiendo cuando un usuario ordinario ha llenado el disco. Consecuencia: un servicio que corre con una cuenta sin privilegios, como la base de datos, ve df marcar 100 % cuando el disco está ocupado en torno al 95 %. De ahí los umbrales elegidos: 80 % para tener tiempo de actuar, 90 % para actuar de inmediato. Esperar al 100 % es avisar después de la caída.
Montar el dispositivo en una hora
Crear el canal de alerta
Cree un bot de Telegram con BotFather, anote su token y el identificador de la conversación que debe recibir los mensajes. Todas las peticiones de la API de Bot siguen la forma
https://api.telegram.org/bot<token>/METHOD_NAME, y el método de envío essendMessage. Si prefiere un webhook (Slack, Mattermost, ntfy), solo cambia la última línea del script de envío.Escribir el archivo de entorno
sudo mkdir -p /var/lib/alerte-vpssudo install -m 600 /dev/null /etc/alerte-vps.env
Añada después cuatro líneas:TG_TOKEN=...,TG_CHAT=...,HC_URL=https://hc-ping.com/<uuid>(check cada cinco minutos) yHC_URL_JOUR=https://hc-ping.com/<otro-uuid>(check diario). El modo 600 deja el token legible solo para root.Escribir el script de envío con antirrepetición
Guarde estas líneas en
/usr/local/bin/alerte-envoiy haga el archivo ejecutable consudo chmod 755. El archivo de bloqueo por mensaje evita que le despierten cada cinco minutos por la misma alerta: un mensaje idéntico solo se reenvía pasada una hora.#!/bin/sh. /etc/alerte-vps.envf=/var/lib/alerte-vps/$(echo "$1" | md5sum | cut -c1-12)[ -n "$(find "$f" -mmin -60 2>/dev/null)" ] && exit 0touch "$f"curl -fsS -m 10 --retry 3 "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" -d chat_id="${TG_CHAT}" --data-urlencode text="$(hostname) $1"Controlar disco e inodos
En
/usr/local/bin/alerte-rapide(shebang#!/bin/sh), añada los dos bucles siguientes. El filtrogrep -vdescarta los sistemas de archivos que no exponen inodos y muestran un guion.df --output=pcent,target -x tmpfs -x devtmpfs | tail -n +2 | while read p m; do n=${p%\%}; if [ "$n" -ge 90 ]; then alerte-envoi "CRIT disk $m"; elif [ "$n" -ge 80 ]; then alerte-envoi "WARN disk $m"; fi; donedf --output=ipcent,target -x tmpfs -x devtmpfs | tail -n +2 | grep -v '^ *-' | while read p m; do [ "${p%\%}" -ge 90 ] && alerte-envoi "INODES $m"; doneDetectar los procesos matados por el OOM killer
El contador
oom_killde/proc/vmstatsolo crece desde el arranque. Se compara con el valor anterior, guardado en un archivo; tras un reinicio el contador vuelve a cero, la comparación falla una vez y luego se restablece.n=$(awk '/^oom_kill/ {print $2}' /proc/vmstat)old=$(cat /var/lib/alerte-vps/oom 2>/dev/null || echo 0)[ "$n" -gt "$old" ] && alerte-envoi "OOM kill"echo "$n" > /var/lib/alerte-vps/oomAñadir el latido y programar
Termine
alerte-rapidecon el ping. Solo se alcanza si el script ha llegado hasta el final; si cron se detiene, si el disco pasa a solo lectura o si el script falla, el ping cesa y el servicio externo le avisa. Añada después las líneas de cron en/etc/cron.d/alerte-vps.. /etc/alerte-vps.env; curl -fsS -m 10 --retry 5 -o /dev/null "$HC_URL"PATH=/usr/local/bin:/usr/bin:/bin*/5 * * * * root /usr/local/bin/alerte-rapide30 7 * * * root /usr/local/bin/alerte-quotidienControlar certificados y copias una vez al día
En
/usr/local/bin/alerte-quotidien, añada estas líneas. La opción-checkenddeopenssl x509falla si el certificado caduca dentro del número de segundos indicado; 1814400 segundos equivalen a 21 días. La prueba de copia avisa cuando ningún archivo.gzno vacío es más reciente que 1560 minutos, es decir 26 horas. El script termina con su propio ping, hacia el segundo check: sin él, un control diario caído en silencio pasaría inadvertido.echo | openssl s_client -servername your-domain.com -connect your-domain.com:443 2>/dev/null | openssl x509 -noout -checkend 1814400 || alerte-envoi "CERT your-domain.com"find /var/backups/app -name '*.gz' -mmin -1560 -size +1k | grep -q . || alerte-envoi "BACKUP absent". /etc/alerte-vps.env; curl -fsS -m 10 --retry 5 -o /dev/null "$HC_URL_JOUR"Conectar los servicios críticos a OnFailure
Cree
/etc/systemd/system/[email protected]con el contenido siguiente y luegosudo systemctl daemon-reload.[Unit]Description=Alerte echec de %i[Service]Type=oneshotExecStart=/usr/local/bin/alerte-envoi "FAILED %i"
Para cada servicio a vigilar, abra un drop-in consudo systemctl edit mariadb.servicey añada estas dos líneas.[Unit]OnFailure=notify-failure@%n.serviceForzar una alerta de prueba y limpiar
Una alerta que nunca se ha disparado no es una alerta. Compruebe primero el espacio libre, cree un archivo lo bastante grande para superar el umbral, ejecute el script a mano y borre el archivo. Si el mensaje no llega, el fallo está en el canal, y es mejor descubrirlo ahora.
df -h --output=pcent,target /var/tmpsudo fallocate -l 2G /var/tmp/test-alerte.imgsudo /usr/local/bin/alerte-rapidesudo rm /var/tmp/test-alerte.img
Memoria: OOM del kernel o límite de cgroup
Dos mecanismos matan un proceso por falta de memoria y no se leen en el mismo sitio. El primero es el OOM killer del kernel, cuando a toda la máquina le falta RAM: el registro del kernel contiene entonces un mensaje del tipo Out of memory: Killed process, que journalctl -k permite encontrar. El segundo es el límite de un cgroup. Según la documentación de systemd, cuando el uso de una unidad no puede contenerse bajo MemoryMax=, el OOM killer se invoca dentro de la unidad: el resto de la máquina puede tener memoria libre.
El contador oom_kill de /proc/vmstat cubre el primer caso a escala de máquina. Para una unidad concreta, el archivo memory.events de su cgroup expone un contador oom_kill propio, definido por la documentación del kernel como el número de procesos del cgroup matados por cualquier tipo de OOM killer. En un VPS que aloja una aplicación voraz, como la descrita en la guía de n8n y OOM en producción, este segundo contador explica las paradas que free -h nunca muestra.
OnFailure: lo que cubre y lo que no
Según la documentación de systemd, OnFailure= enumera las unidades que se activan cuando la unidad entra en el estado failed. La plantilla [email protected] recibe el nombre de la unidad defectuosa gracias al especificador %n, y el mensaje de alerta puede añadir la salida de systemctl status o de journalctl -u como contexto.
El límite es estructural: un servicio que funciona pero ya no responde nunca pasó por el estado failed, así que OnFailure= no dice nada. Un servicio relanzado por Restart= solo llega ahí cuando se alcanza el límite de arranque. Para esos casos, añada una sonda de aplicación sencilla, por ejemplo un curl a la dirección de salud de la aplicación, en el script rápido. La sintaxis de timers y cron se trata en la guía de cron y timers de systemd, y no hace falta repetirla aquí.
Certificados y copias: avisar por la ausencia de éxito
Una renovación automática de certificado puede fallar en silencio durante semanas: un registro DNS modificado, un puerto bloqueado, un complemento roto. El certificado no caduca ese día, caduca noventa días después, y nadie se acuerda. La prueba con openssl interroga lo que el visitante recibe realmente, lo cual vale más que leer un archivo del disco. En un servidor gestionado con Certbot, certbot certificates lista los certificados y su vencimiento, y certbot renew --dry-run comprueba que la próxima renovación saldrá bien.
Misma lógica para las copias de seguridad. Una alerta disparada por el fallo de un script de copia no ve el caso en que el script nunca se ejecutó. La alerta correcta se coloca sobre la ausencia: ningún archivo reciente, o un archivo de tamaño cero. Eso hace la línea find -mmin del script diario, y por eso calla cuando todo va bien y habla en cuanto la cadena de copias se detiene, sea cual sea el motivo.
Scripts propios o pila de supervisión
Desplace la tabla
| Criterio | Scripts shell y OnFailure | Beszel o Uptime Kuma |
|---|---|---|
| Puesta en marcha | Una hora, unos pocos archivos en `/usr/local/bin` | Un hub que alojar, agentes que instalar en cada VPS |
| Parque objetivo | De 1 a 5 VPS, hasta 10 si los umbrales son idénticos | Más allá de 5 a 10 VPS, o en cuanto se necesite comparar |
| Historial y gráficas | Ninguno, solo el instante de la alerta | Gráficas, tendencias, comparación entre máquinas |
| Mantenimiento | Nada que actualizar fuera del shell | Un segundo servidor y sus actualizaciones que mantener |
| Servicios de aplicación | Una sonda `curl` que escribir uno mismo | Sondas HTTP y TCP y página de estado listas para usar |
| Caída del monitor | Detectada por el latido externo | A vigilar aparte, el hub es a su vez una máquina |
| Cuándo elegirlo | Alertas mínimas, parque pequeño, ningún servidor extra | Historial deseado, parque en crecimiento, equipo compartido |
Vigile al vigilante
La trampa de estos scripts es convertirse en un falso seguro: callan porque todo va bien, o porque están rotos, y el silencio se parece en ambos casos. El ping de latido resuelve el problema. Ajuste el periodo esperado del check un poco por encima del intervalo de cron y añada un margen de tolerancia: una alerta de silencio que llega veinte minutos tarde vale más que un falso positivo en cada reinicio. Mantenga también /etc/alerte-vps.env en modo 600 y no versione nunca el token.
Solución de problemas: los errores que vuelven
Las propias alertas fallan de manera previsible. Estos son los casos que se encuentran con más frecuencia, con el mensaje o síntoma que reconocer y el remedio. Cada uno se comprueba a mano antes de buscar más lejos.
Mensajes y síntomas con su remedio
PANIC: could not write to file "pg_wal/...": No space left on device— el volumen que contiene los registros de PostgreSQL está lleno. Libere primero espacio fuera de PostgreSQL (registros rotados, copias antiguas), no borre nunca a mano los archivos depg_waly reinicie después el servicio.No space left on devicemientrasdf -hindica espacio — los inodos están agotados. Ejecutedf --output=ipcent,target, localice la carpeta con millones de archivos pequeños (sesiones, caché) y purgue.- Disco lleno pero
duno encuentra el espacio — archivos borrados siguen abiertos por un proceso.sudo lsof +L1los lista; reiniciar el proceso libera el espacio. oom_killaumenta, nada enjournalctl -k— el registro del kernel rotó o la muerte viene de un límiteMemoryMax=. Leamemory.eventsen el directorio del cgroup de la unidad yjournalctl -upara la unidad.- Una unidad funciona pero ya no responde, sin alerta — es el límite de
OnFailure=. Añada una sondacurla la dirección de salud y avise según el código de retorno. - Ningún mensaje de Telegram, ningún error visible — cron envía la salida del script por correo local, que nadie lee. Ejecute
alerte-envoi testa mano consudoy lea la respuesta decurl. - El certificado se acerca al vencimiento aunque Certbot esté instalado — ejecute
certbot renew --dry-runy lea el error de validación; corrija después el DNS o el puerto implicado.
La documentación oficial
Cada comportamiento descrito aquí procede de una fuente primaria. Para OnFailure=, la página de manual de systemd.unit; para MemoryMax=, systemd.resource-control; para los contadores oom y oom_kill, la documentación de cgroups v2 del kernel; para el 5 % reservado, la página de manual de tune2fs. En el envío, la API de Bot de Telegram describe la forma de las peticiones, y la documentación de la API de Healthchecks las direcciones de ping, con la página de precios para los límites del plan gratuito. Para la renovación, la documentación de Certbot. El mensaje de PostgreSQL citado procede de un hilo de la lista oficial del proyecto.
Alertas sencillas, las mismas en cada VPS
Seis alertas y un latido no sustituyen una pila de supervisión: cubren lo que realmente tumba un servidor: disco, inodos, memoria, servicios, certificados, copias. La ganancia principal es la uniformidad: mismos umbrales, mismos mensajes, mismo gesto de prueba en cada máquina del parque. Cuando el parque crece o hace falta historial, el salto a una pila se prepara en la guía de Beszel y en la guía de alertas proactivas con Beszel y Uptime Kuma, que tratan la instalación de principio a fin.