On this page
- How NUT protects a multi-machine homelab
- Task 1: Install and enable NUT on the primary
- Task 2: Point NUT at your UPS
- Task 3: Open the network and add a monitor account
- Task 4: Tell upsmon when and how to shut down
- Task 5: Protect the other machines (the secondaries)
- The shutdown sequence, in order
- Common gotchas
- What’s next
The power flickers, the lights come back, and you go check on the lab — one Proxmox node is fscking a corrupted root, a VM won’t boot, and your NAS is resilvering. A UPS (uninterruptible power supply — a battery that carries your gear through a blip or gives you a few minutes to shut down) stops the hard yank, but the battery only lasts a few minutes. If nobody is home to run shutdown, those minutes run out and everything drops at once anyway.
Network UPS Tools (NUT) turns those minutes into an automatic, orderly shutdown of your entire homelab — from a single UPS with a single data cable. This guide wires up one machine as the NUT server and the rest as network clients, so a power cut ends with a clean shutdown of every box instead of a filesystem check in the morning.
How NUT protects a multi-machine homelab
NUT splits into three small daemons, and the whole design follows one physical fact: only the machine with the data cable can talk to the UPS.
- The driver (for USB units,
usbhid-ups) talks to the UPS hardware and translates it into NUT variables likeups.status: OL(on line) orOB(on battery). upsdis the network server. It publishes those variables on TCP port 3493 so other machines — and you — can read them.upsmonis the monitor. It watches the status and, when the battery goes critical, runs your shutdown command.
The machine wired to the UPS runs all three and is the primary. Every other machine on that same UPS runs only upsmon, pointed at the primary over the network — that’s a secondary. (NUT renamed these from master/slave in version 2.8.0; the old keywords still work but the docs use primary/secondary now.)
When the power fails, the sequence is deliberate: the secondaries shut down first, and the primary shuts down last — because the primary is the only one that can tell the UPS to cut its own power, and it must not do that until everyone else is safely down.
NUT does not need a big UPS or a fancy one — it needs a UPS with a data port (USB on most consumer units). A bargain UPS with no data cable can hold the power up, but no software can see its battery, so nothing can trigger an automatic shutdown. If you’re buying, that data port is the feature that matters here.
Task 1: Install and enable NUT on the primary
Do this on the one machine physically connected to the UPS by its USB cable — a Proxmox host, a NAS, or a dedicated always-on box.
This guide uses example values you must replace: the UPS name myups, the primary’s LAN address 10.0.0.10, the monitor account monuser with password CHANGE_ME, and an admin password CHANGE_ME_TOO. Your secondaries live at addresses like 10.0.0.11 and 10.0.0.12. Put the real passwords in your own secret store, not in a note — and if a value looks specific to one machine (an IP, a hostname, a password), it’s a placeholder to change, not a literal to copy.
On Debian or Ubuntu, nut-server brings the driver and upsd; nut-client brings upsmon and the command-line tools. Install both on the primary:
apt update && apt install -y nut-server nut-client
Verify the version — anything 2.8.0 or newer uses the primary/secondary terminology in this guide (Debian 13 ships 2.8.1):
upsc -V
With the UPS plugged into the wall and its USB cable into this machine, ask NUT to find it:
nut-scanner -U
nut-scanner prints a ready-made ups.conf block naming the driver it detected (almost always usbhid-ups for USB units). If it finds nothing, re-seat the USB cable and confirm the OS sees it with lsusb.
If you previously installed apcupsd or another UPS tool, stop and disable it first. Two programs cannot both hold the USB device — the second one gets “device busy” and the driver fails to start.
/etc/nut/nut.conf has a single meaningful line, MODE, and it ships as MODE=none so nothing starts by accident. The primary serves the UPS to the network, so set it to netserver:
# MODE=none (default — nothing runs)
# MODE=standalone (this box only, no network clients)
# MODE=netserver (this box owns the UPS AND serves clients) <- use this
# MODE=netclient (no local UPS; only watches another server)
echo "MODE=netserver" > /etc/nut/nut.conf
Task 2: Point NUT at your UPS
Tell the driver which UPS to talk to. Use the block nut-scanner printed, or write it by hand. For a USB unit:
cat > /etc/nut/ups.conf <<'EOF'
[myups]
driver = usbhid-ups
port = auto
desc = "Homelab UPS"
EOF
port = auto is correct for USB — the driver finds the device itself; the value is only meaningful for old serial units. The name in brackets, myups, is how every other config and command refers to this UPS.
Restart the server so the driver connects, then read the live values back with upsc:
systemctl restart nut-server
upsc myups
You should see a dump of variables. The two that matter most:
upsc myups ups.status
# OL = On Line (running on mains)
# OB = On Battery (mains lost)
# OB LB = On Battery, Low Battery (critical — shutdown territory)
If upsc prints Error: Driver not connected, the driver couldn’t reach the UPS — recheck the USB cable and the driver name in ups.conf.
Task 3: Open the network and add a monitor account
By default upsd listens on localhost only — so a fresh install works on the primary but every secondary gets connection refused. You have to open it up explicitly and give the clients an account.
Add a LISTEN line for the primary’s own LAN address (keep localhost too, so local tools still work):
cat > /etc/nut/upsd.conf <<'EOF'
LISTEN 127.0.0.1 3493
LISTEN 10.0.0.10 3493
EOF
List the specific addresses you want, or a single LISTEN 0.0.0.0 3493 to listen everywhere. Do not combine LISTEN 0.0.0.0 3493 with LISTEN 127.0.0.1 3493 — the ranges overlap, one bind loses, and upsd ends up listening only on localhost while looking like it started fine. Bind localhost plus your real LAN IP, as above.
Every upsmon — the primary’s own and each secondary’s — logs into upsd with an account. Create a monitor user, and an admin user you’ll use to test:
cat > /etc/nut/upsd.users <<'EOF'
[monuser]
password = CHANGE_ME
upsmon primary
[admin]
password = CHANGE_ME_TOO
actions = SET
instcmds = ALL
EOF
chown root:nut /etc/nut/upsd.users
chmod 640 /etc/nut/upsd.users
The upsmon primary line grants this account the primary role. The file holds passwords, so lock it down to root:nut mode 640 — the upsd process runs as the nut user and reads it by group.
Task 4: Tell upsmon when and how to shut down
upsmon.conf is where the policy lives: which UPS to watch, and what to run when it’s critical.
cat > /etc/nut/upsmon.conf <<'EOF'
MONITOR myups@localhost 1 monuser CHANGE_ME primary
MINSUPPLIES 1
SHUTDOWNCMD "/sbin/shutdown -h +0"
POWERDOWNFLAG /run/nut/killpower
EOF
chown root:nut /etc/nut/upsmon.conf
chmod 640 /etc/nut/upsmon.conf
Reading the MONITOR line field by field — the order is fixed:
| Field | Value | Meaning |
|---|---|---|
| UPS | myups@localhost |
the UPS name, and where its upsd runs |
| power value | 1 |
how many power supplies this machine draws from it |
| username | monuser |
account from upsd.users |
| password | CHANGE_ME |
its password |
| role | primary |
this box manages the UPS |
MINSUPPLIES 1 means “this machine needs at least one working supply” — a normal single-PSU box. SHUTDOWNCMD is what runs when the battery is critical. POWERDOWNFLAG is a small file upsmon drops on its way down; the system’s late-shutdown step sees it and runs upsdrvctl shutdown, which tells the UPS itself to cut power — so the UPS power-cycles and your machines boot back up when mains returns.
systemctl restart nut-server nut-monitor
systemctl status nut-monitor --no-pager
On Debian the monitor service is nut-monitor (nut-client is an alias for it). It should be active (running) and, in journalctl -u nut-monitor, log Communications with UPS myups@localhost established.
Task 5: Protect the other machines (the secondaries)
Now repeat on every other machine running on that UPS — the other Proxmox nodes, the NAS, anything with a plug in the battery side. These never touch the UPS hardware; they only watch the primary.
On a secondary you only need the client package:
apt update && apt install -y nut-client
echo "MODE=netclient" > /etc/nut/nut.conf
The only real difference from the primary: the UPS is myups@10.0.0.10 (the primary’s address, not localhost), and the role is secondary:
cat > /etc/nut/upsmon.conf <<'EOF'
MONITOR myups@10.0.0.10 1 monuser CHANGE_ME secondary
MINSUPPLIES 1
SHUTDOWNCMD "/sbin/shutdown -h +0"
EOF
chown root:nut /etc/nut/upsmon.conf
chmod 640 /etc/nut/upsmon.conf
systemctl restart nut-monitor
Confirm the secondary can read the UPS across the network — this is the moment the LISTEN line from Task 3 pays off:
upsc myups@10.0.0.10 ups.status
OL means it’s connected and watching. Connection refused means the primary isn’t listening on the LAN (recheck upsd.conf); an access error means the username or password doesn’t match upsd.users.
The shutdown sequence, in order
This is the payoff, and it’s worth knowing exactly what happens so you trust it. When mains fails, the UPS reports OB (on battery) and NUT logs an ONBATT event on every machine — but nothing shuts down yet; you’re just running on battery. When the charge falls to the low-battery threshold, the status becomes OB LB and it turns critical:
In a real run against a NUT server and one network client, the primary logs Host sync timer expired, forcing shutdown after waiting the HOSTSYNC window for the client to drop, then Executing automatic power-fail shutdown — a couple of seconds after the secondary has already gone. That ordering is the entire point: nothing races, and the machine that controls the UPS is the last to leave.
upsmon -c fsd forces the exact critical-shutdown path on demand — the real thing, so it will actually power your machines down. Test on a scratch machine, or when you can afford the reboot, and watch journalctl -u nut-monitor -f in another window. Never run it for the first time on a box you can’t afford to lose. To rehearse the logic without real hardware at all, NUT ships a dummy-ups driver whose status you can flip by hand.
Common gotchas
upsdlistens on localhost by default. The single most common “my clients can’t connect” cause. Add theLISTENline (Task 3) and restartnut-server.0.0.0.0and127.0.0.1together bind only localhost. Overlapping listen addresses silently collapse to one. Use distinct addresses or a lone0.0.0.0.- Config files are
root:nutmode640. They hold passwords; if you hand-edit and the perms drift,upsdmay refuse to read them. - Only one UPS tool at a time.
apcupsdand NUT both grab the USB device; disable one. POWERDOWNFLAGis mandatory in primary mode.upsmonhas no built-in default; without the line, the “cut UPS power” step can’t fire.
What’s next
With a UPS watched and a clean shutdown wired up, the natural next step is knowing the moment it kicks in: point Uptime Kuma or your Grafana and Prometheus stack at the events, and make sure the machines this protects are themselves backed up with Proxmox Backup Server. Want the whole setup as one copy-paste script? It’s in the NUT network-shutdown playbook.
Related posts:
- Proxmox Backup Server: Automated CT and VM Backups — the other half of resilience; a clean shutdown protects today, backups protect the bad day
- Uptime Kuma: Dead-Simple Homelab Monitoring — get a phone alert the moment the UPS goes on battery
- Grafana + Prometheus for Proxmox — graph battery charge and runtime over time
- Proxmox Security Hardening — more ways to keep the nodes this UPS protects healthy
- Proxmox Cluster Setup: Quorum, Corosync, and Node Joining — the multi-node cluster that makes one-UPS-many-clients worth doing
Recommended hardware for this setup:
- Line-interactive UPS with USB (CyberPower CP1500PFCLCD) — sine-wave output and a USB data port NUT reads out of the box
- APC Back-UPS Pro with USB — another well-supported
usbhid-upsunit - Eaton 5S UPS — compact, USB, and on the NUT compatibility list
Sources: Network UPS Tools — Configuration notes, upsmon.conf(5) manual, dummy-ups(8) manual, NUT Hardware Compatibility List.
This post contains Amazon affiliate links (tag: buildahomelab-20). I earn a small commission on qualifying purchases at no extra cost to you.
Comments
Comments are powered by GitHub Discussions — sign in with a GitHub account to join the conversation.