Problema
En entornos con varios nodos Proxmox, la pérdida de energía suele desencadenar apagados descoordinados. Cada host intenta cerrar sus máquinas virtuales de forma independiente, lo que genera situaciones donde el clúster queda parcialmente activo: algunos nodos siguen con HA habilitado, otros ya están apagados y los discos compartidos (Ceph, ZFS) pierden quorum. Además, la falta de una capa de notificación hace que el equipo de operaciones no reciba alertas tempranas de la caída de la UPS. El reto es conectar la UPS al ecosistema Proxmox de modo que:
- La UPS sea monitorizada sin instalar agentes en cada nodo.
- Las notificaciones se envíen a los canales habituales (Slack, Discord, ntfy, Teams).
- En clústeres, el apagado sea ordenado: desactivar HA, detener VMs, marcar mantenimiento y, finalmente, apagar los hosts.
- Se pueda usar un Proxmox Backup Server (PBS) como objetivo de apagado cuando la energía se agota.
Causa
Los fallos típicos provienen de tres áreas:
- Monitoreo distribuido – Configurar
upsmono NUT en cada nodo genera archivos de configuración duplicados y puntos de falla. Cuando la UPS cambia de puerto USB a SNMP, todos los nodos deben actualizarse manualmente. - Descoordinación de HA – En clústers sin una fase previa de desarme, el gestor de alta disponibilidad (HA) vuelve a intentar re‑schedulear VMs en nodos que ya están en proceso de apagado, provocando bucles de reinicio o I/O bloqueado.
- Notificaciones limitadas – Los scripts de shutdown tradicionales solo escriben en syslog. Sin un webhook flexible, los operadores no pueden filtrar la gravedad ni enviar mensajes a la herramienta de chat que usan.
Solución
Una solución reutilizable combina tres componentes:
- Contenedor o LXC de monitorización UPS (pve‑ups) – Un único proceso que consulta la UPS vía SNMP, NUT o API propietaria y expone un endpoint HTTP para que Proxmox lo invoque. No requiere
upsmonen cada nodo. - Webhook manager integrado – Permite definir varios destinos, cada uno con su propio formato (JSON, Slack, Discord, ntfy, Teams) y filtro de severidad. Los encabezados de autorización se almacenan como secretos.
- Módulo de apagado consciente de clúster – Antes de iniciar el apagado, el contenedor ejecuta una serie de pasos:
- Desarma HA a nivel de clúster (
pvecm status --disable-hao ajuste de flags en Ceph). - Detiene localmente todas las VMs del nodo que está a punto de apagarse.
- Marca el nodo en modo mantenimiento (
pvecm setmaintenance <node>). - Si el objetivo es PBS, genera un token válido y llama a la API de PBS para registrar el apagado.
- Apaga el host mediante la API de Proxmox (
/nodes/<node>/status/shutdown).
- Desarma HA a nivel de clúster (
Implementación paso a paso
-
Despliegue del contenedor
docker pull ffind/pve-ups:latest docker run -d \ --name pve-ups \ -p 8080:8080 \ -v /etc/pve-ups:/config \ --restart unless-stopped \ ffind/pve-ups:latestEl volumen
/etc/pve-upscontieneconfig.yamlcon la información de la UPS (IP, comunidad SNMP, credenciales NUT) y la lista de nodos objetivo. -
Configurar webhooks
Enconfig.yamlañada una secciónwebhooks:con entradas como:webhooks: - name: slack-alert url: https://hooks.slack.com/services/T000/B000/XXXX format: slack severity: warning auth_header: "Bearer ${SLACK_TOKEN}" - name: ntfy url: https://ntfy.sh/pve-ups format: json severity: infoCada webhook tiene su propio filtro; los eventos críticos (batería < 20 %) se envían a todos los destinos.
-
Activar soporte PBS
Añada a la configuración del nodo objetivo:shutdown_targets: - type: pve node: node01 - type: pbs url: https://pbs01:8007/api2/json token: ${PBS_TOKEN}El contenedor valida el token contra la API de PBS antes de iniciar el apagado.
-
Habilitar modo clúster (beta)
En la UI de PVE‑UPS active la opción “Cluster‑aware shutdown”. El contenedor verificará la versión de Proxmox (>=9.2) y, si está en un clúster, ejecutará los pasos de desarme HA y mantenimiento antes de cualquier apagado individual. -
Pruebas en seco
La UI incluye un “dry‑run” que simula todo el flujo sin apagar los hosts. Use el botón “Run self‑test now” para validar la lógica después de cualquier cambio de configuración.
Cuándo aplicar esta solución
Aplica cuando:
- Tiene al menos dos nodos Proxmox bajo HA y necesita que el apagado sea coordinado.
- La UPS está expuesta vía SNMP o NUT y no quiere instalar agentes en cada host.
- Requiere notificaciones a varios canales de chat y necesita filtrar por severidad.
- Usa Proxmox Backup Server como repositorio de copias y quiere que reciba la señal de apagado.
No aplica si:
- El entorno es un único nodo sin HA; el proceso de desarme de clúster es innecesario.
- La UPS solo se monitorea con
upsmony no hay requerimientos de webhook. - No se dispone de acceso a Docker/LXC en el host de gestión.
Código
# Verificar que el contenedor está corriendo y escuchando
curl -s http://localhost:8080/health | grep alive
# Simular una caída de batería (valor bajo para pruebas)
curl -X POST http://localhost:8080/simulate \
-H "Content-Type: application/json" \
-d '{"battery_percent":15}'
Verificación
- Estado del contenedor –
docker logs pve-upsdebe mostrar “initialized” y la lista de nodos detectados. - Webhook recibido – Revise el canal Slack o la cola ntfy; debe aparecer un mensaje con la etiqueta “warning” y la carga de la batería.
- Desarme HA – En la UI de Proxmox, el clúster debe mostrar “HA disabled” antes de que cualquier nodo se apague.
- Apagado ordenado – Después del test de simulación, los nodos deben pasar a “maintenance” y luego a “offline”. Verifique que PBS registra el evento en su historial de tareas.
- Restauración – Use el botón “Restore cluster” o
pvecm setmaintenance <node> offpara volver a habilitar HA. Confirme que los nodos vuelven a “online”.
Notas adicionales
- Persistencia de tokens – Guarde los tokens de PBS y los encabezados de autorización en un gestor de secretos (Vault, sops) y referéncialos con variables de entorno en
config.yaml. Evita hardcodearlos. - Límites de SNMP – Algunas UPS limitan la frecuencia de polling. Ajuste
poll_intervala 30 s o más para evitar bloqueos de la comunidad SNMP. - Ceph vs ZFS – Cuando el clúster usa Ceph, el desarme HA también debe establecer los flags
nooutynodownpara evitar rebalanceos inesperados durante el apagado. - Actualizaciones – La actualización del contenedor es tan simple como
docker pull+docker restart. No hay cambios de esquema en la base de datos; la compatibilidad con versiones 3.x está garantizada. - Modo de emergencia – Si la UPS deja de responder, el contenedor pasa automáticamente a “force‑shutdown” después de un timeout configurable (default 5 min). Revise los logs para asegurarse de que no haya falsos positivos.