Tener un sistema de respaldo eléctrico (UPS) es fundamental en servidores domésticos que alojan múltiples servicios. Pero no basta con que la UPS suministre energía unos minutos: lo crítico es que, en caso de corte prolongado, todo el sistema se apague de forma controlada y ordenada. En este artículo documento cómo he desplegado una solución basada en NUT sobre Proxmox para conseguirlo.
En este ejemplo verás que mi servidor NUC está conectado por cable USB a la UPS. Parte de los servicios a apagar son críticos (uno es una VM con el router linux a internet, el otro es el que da servicios DNS/DHCP). Lo que hago es crear un contenedor (LXC) especial dedicado a NUT (Network UPS Tools), al que le paso la USB y se va a encargar de todo, incluso de apagar a su propio Host proxmox…
Introducción
Creo y configuro el nuevo LXC llamado nut
con el fin de:
- Monitorizar el estado de una UPS conectada por USB.
- Exponer el estado a Home Assistant (hass), para “monitorizar” a la UPS.
- Ejecutar un apagado ordenado de todo lo que corre en el NUC (proxmox) ante fallo eléctrico prolongado.
Ejemplo de Topología
graph TD UPS["UPS (USB)"] --> nut nut["LXC nut<br>192.168.1.3"] pihole["VM pihole<br>192.168.1.2"] router["VM router<br>192.168.1.1"] wlc["VM wlc"] hass["VM hass"] subgraph proxmox_host [proxmox 192.168.1.254] nut pihole router hass wlc end
PROXMOX: Instalo el contenedor LXC
Lo puedes hacer desde el UI, yo me bajé el template de debian 12. Si prefieres desde línea de comando:
pct create 123 local:vztmpl/debian-12-standard_12.0-1_amd64.tar.zst \
-hostname nut \
-memory 512 \
-net0 name=eth0,bridge=vmbr0,ip=192.168.1.3/24,gw=192.168.1.1 \
-rootfs local-lvm:8 \
-features nesting=1 \
-unprivileged 1
pct start 123
pct exec 123 -- bash
Configura desde el administrador de Proxmox que el dispositivo USB se le asigne al nuevo LXC nut
. Para comprobar que lo has hecho bien, mira la configuración dentro de proxmox
del LXC en /etc/pve/lxc/123.conf
, deberías tener algo como lo siguiente (comprueba con lsusb
dónde tienes a la UPS)
lxc.cgroup2.devices.allow: c 189:* rwm
lxc.mount.entry: /dev/bus/usb/001/003 dev/bus/usb/001/003 none bind,optional,create=file
Parametrizo el LXC nut
:
Arranco el nuevo contenedor y entro en su shell como root.
apt update && apt full-upgrade -y
Dentro del nuevo LXC instalonut
:
Averiguo dónde me pasa proxmox el dispositivo usb. En mi caso lo tendré disponible en /dev/bus/usb/001/003
*.
root@nut:~# lsusb
:
Bus 001 Device 003: ID 0001:0000 Fry's Electronics MEC0003
:
Instalo nut
apt update && apt full-upgrade -y
apt install nut nut-client -y
Fichero /etc/nut/nut.conf
:
MODE=standalone
Fichero /etc/nut/ups.conf
[ups]
driver = blazer_usb
port = /dev/bus/usb/001/003
desc = "Fry's Electronics MEC0003"
Fichero /etc/nut/upsd.conf
LISTEN 127.0.0.1 3493
LISTEN 192.168.1.3 3493
Fichero /etc/nut/upsd.users
[homeassistant]
password = upspass
upsmon master
instcmds = all
Fichero /etc/nut/upsmon.conf
MONITOR ups@localhost 1 homeassistant upspass master
MINSUPPLIES 1
SHUTDOWNCMD "/usr/local/sbin/nut-shutdown-master.sh"
POLLFREQ 5
POLLFREQALERT 5
HOSTSYNC 15
DEADTIME 15
POWERDOWNFLAG /etc/killpower
RBWARNTIME 43200
NOCOMMWARNTIME 300
FINALDELAY 5
Permisos en contenedor LXC:
En contenedores no privilegiados (como es el caso), tanto el driver de NUT (blazer_usb) como el daemon no puede acceder al dispositivo USB y el directorio /run
por las restricciones de seguridad que impone este tipo de contenedor. Por lo tanto haremos un truco.
Primero manualmente ejecuta:
mkdir -p /run/nut
chown nut:nut /run/nut
chown nut:nut /dev/bus/usb/001/003
Es necesario poner los permisos de nuevo en cada arranque, para conseguirlo creo un script: /etc/rc.local
#!/bin/sh
# Fichero /etc/rc.local que llamaré desde systemd
mkdir -p /run/nut
chown nut:nut /run/nut
chown nut:nut /dev/bus/usb/001/003
exit 0
Y un servicio en systemd para que lo llame al arrancar: /etc/systemd/system/rc-local.service
# Mini servicio que ejecuta /etc/rc.local durante el arranque
[Unit]
Description=Ejecutar /etc/rc.local
ConditionPathExists=/etc/rc.local
After=network.target
[Service]
Type=forking
ExecStart=/etc/rc.local
TimeoutSec=0
RemainAfterExit=yes
GuessMainPID=no
[Install]
WantedBy=multi-user.target
Programo que arranque siempre
systemctl daemon-reload
systemctl enable rc-local.service
Arranque y prueba de servicios:
systemctl daemon-reload
systemctl enable nut-server
systemctl enable nut-monitor
systemctl start nut-server
systemctl start nut-monitor
Verifica que todo funciona bien
systemctl status nut-server
systemctl status nut-monitor
upsc ups@localhost
Integración en Home Assistant (HASS):
- Settings > Devices & Services > “Network UPS Tool (NUT)”.
- Configuro nombre, direccion IP del LXC
nut
(192.168.1.3
), usuariohomeassistant
y contraseñaupspass
- Configuro nombre, direccion IP del LXC
Configuración SSH:
Para que nut
pueda acceder sin contraseña a los equipos: proxmox, router, pihole
, necesito dar de alta las claves SSH correctamente, tienen que confiar en “nut” para dejarle que ejecute comandos a nivel de root (para poder hacer el apagado ordenado).
En nut:
ssh-keygen -t ed25519 -f /root/.ssh/nut_shutdown_key
Luego añadir el contenido de la clave pública (nut_shutdown_key.pub
) a los siguientes ficheros:
- router (192.168.1.1) ->
/root/.ssh/authorized_keys
- pihole (192.168.1.2) ->
/root/.ssh/authorized_keys
- proxmox (192.168.1.254) ->
/etc/pve/priv/authorized_keys
Verificar que los tres tienen bien configurado /etc/ssh/sshd_config
para que acepte autenticación por clave pública. Ejemplo (muy permisivo): permite acceso root directo, solo recomendable en entornos de confianza. De hecho se podría mejorar creando usuarios y usando sudo, ten en cuenta que esto es solo un ejemplo.
# Config para sshd que acepta ssh vía root.
# primero intenta clave public/private y si no va pide contraseña.
Port 22
PubkeyAuthentication yes
PasswordAuthentication yes
PermitRootLogin yes
UsePAM no
AllowAgentForwarding yes
AllowTcpForwarding yes
GatewayPorts yes
X11Forwarding yes
X11DisplayOffset 10
X11UseLocalhost yes
AddressFamily inet
PrintMotd no
PrintLastLog no
Subsystem sftp /usr/lib64/misc/sftp-server
AcceptEnv LANG LC_*
Creo el script de apagado:
A continuación tienes la chicha, el script que va a ejecutar nut
cuando detecta un fallo prolongado en la alimentación:
- Apagado remoto de pihole y router por SSH.
- Apagado de VMs hass y wlc desde Proxmox (al que le da las órdenes por SSH).
- Apagado del host Proxmox.
- Apagado del contenedor nut (poweroff).
Fíjate que nut
es el que manda, usa IPs para evitar dependencia de DNS y este script se dispara automáticamente ante fallo crítico de alimentación.
Fichero /usr/local/sbin/nut-shutdown-master.sh
:
Referencias
- https://networkupstools.org/
- man upsd, man upsmon, man ups.conf