UPS Monitoring with NUT: Make Your Homelab Shut Itself Down
Set up Network UPS Tools on Proxmox so your homelab shuts down cleanly on battery. USB and SNMP drivers, upsmon, shutdown timing, secondary nodes, and how to test it without faking it.
This guide contains no paid affiliate links. It is configuration, not shopping. If you are still choosing a unit, the UPS buying guide covers sizing and picks.
A UPS that your server cannot talk to is a surge protector with a heavy battery in it. It will ride out flickers and brownouts, which is genuinely worth something, but when the power stays out it will run the battery flat and then drop the machine exactly as hard as the outage would have. The piece that turns a battery into actual protection is software, and on Linux that software is Network UPS Tools. This guide wires NUT into a Proxmox host, which pairs with the rest of the post-install checklist as the step most people skip.
The plan is straightforward: get the UPS talking to one machine, decide how long you are willing to sit on battery before giving up, make the host shut itself down inside that window, and then prove it works by pulling the plug. If more than one machine shares the UPS, the same install feeds the others over the network. Power draw matters here too, because runtime is a function of load, and the homelab power usage numbers are what tell you how much time you actually have.
Every address below is a placeholder. Substitute your own.
Quick Answer
# install on the machine with the UPS cable
sudo apt install nut-server nut-client
# find the UPS and get a ready-made config block
sudo nut-scanner -U
# after editing /etc/nut/ups.conf, nut.conf, upsd.users, upsmon.conf:
sudo systemctl restart nut-server nut-monitor
# confirm it is reading the UPS
upsc homelab-ups@localhost
That is the whole shape of it. The rest of this guide is the difference between a config that reports battery percentage and a config that saves your filesystem at 2am.
What You Need First
- A UPS with a data port. USB on consumer units, a network management card on used enterprise gear. No data port means no shutdown, and no amount of software fixes that.
- One Linux machine physically connected to it. On a Proxmox box, that is the host, not a VM. Passing a USB UPS through to a guest and asking that guest to shut the host down is a chain with an obvious weak link.
- Root access and about ninety minutes, most of which is testing.
Step 1: Connect and Identify the UPS
Plug the USB cable in, then check the kernel saw it:
lsusb
Look for the vendor name. Most consumer units appear as a generic HID device from APC, CyberPower, Eaton, or Tripp Lite.
Install NUT:
sudo apt update
sudo apt install nut-server nut-client
Then let NUT work out the driver for you:
sudo nut-scanner -U
nut-scanner prints a config block ready to paste, including the correct driver name. This is worth doing even if you think you know the driver, because getting it wrong is the single most common reason a NUT setup reports a UPS that never changes status.
The drivers you will actually encounter:
| Driver | Typical hardware |
|---|---|
usbhid-ups | Most modern USB units: APC Back-UPS, CyberPower, Eaton |
blazer_usb | Cheaper generic and rebadged units |
nutdrv_qx | Megatec/Q1 protocol units, the successor to blazer_usb |
snmp-ups | Anything with a network management card |
dummy-ups | Testing and relaying, no real hardware |
Step 2: Define the UPS
Edit /etc/nut/ups.conf:
[homelab-ups]
driver = usbhid-ups
port = auto
desc = "Homelab UPS"
pollinterval = 5
For a network-managed unit instead:
[homelab-ups]
driver = snmp-ups
port = 192.168.1.20
community = public
snmp_version = v1
desc = "Rack UPS via NMC"
Test the driver before touching any services:
sudo upsdrvctl start
A clean attach looks unremarkable, which is the point. If it fails, run it verbose and read the actual error rather than guessing:
sudo upsdrvctl -D start
Permission errors on USB are the other common failure. The NUT packages ship udev rules that hand the device to the nut user, but those rules apply on device connection, so unplugging the USB cable and plugging it back in after install fixes a surprising number of cases.
Step 3: Set the Mode and Start upsd
/etc/nut/nut.conf decides what this machine is:
MODE=standalone
Use standalone when one machine owns the UPS and nothing else needs to hear about it. Use netserver when other machines on the UPS will monitor it over the network. Those are the only two you need; netclient belongs on the other machines, covered later.
Start the server:
sudo systemctl enable --now nut-server
upsc homelab-ups@localhost
You should get a block of variables:
battery.charge: 100
battery.runtime: 2880
ups.load: 12
ups.status: OL
Those four lines are the ones that matter. ups.status is the state machine everything else keys off:
| Status | Meaning |
|---|---|
OL | Online, running on mains |
OB | On battery |
LB | Low battery, as defined by the UPS firmware |
OL CHRG | On mains, recharging |
FSD | Forced shutdown, someone or something called it |
Step 4: Create upsd Users
/etc/nut/upsd.users controls who may talk to the server. Create the monitoring account:
[monuser]
password = change-this-to-something-random
upsmon primary
If you also want to run instant commands like a self-test, add a separate admin account rather than granting the monitor extra rights:
[upsadmin]
password = a-different-random-string
actions = SET
instcmds = ALL
Older documentation says master and slave where current NUT says primary and secondary. Recent releases accept both, but write the current keywords; you will not have to think about it again.
Lock the file down and restart:
sudo chown root:nut /etc/nut/upsd.users
sudo chmod 640 /etc/nut/upsd.users
sudo systemctl restart nut-server
Step 5: Configure upsmon
This is the file that does the work. In /etc/nut/upsmon.conf:
MONITOR homelab-ups@localhost 1 monuser change-this-to-something-random primary
MINSUPPLIES 1
SHUTDOWNCMD "/sbin/shutdown -h +0"
POLLFREQ 5
POLLFREQALERT 5
HOSTSYNC 15
DEADTIME 15
POWERDOWNFLAG /etc/killpower
FINALDELAY 5
The parts that decide behaviour:
MONITOR: the UPS, how many power supplies this machine draws from it (1for anything normal), the credentials, and this machine’s role.SHUTDOWNCMD: run when NUT decides it is time. On Proxmox this must be a real system shutdown, because that is what triggers the guest-stopping sequence.POWERDOWNFLAG: the file whose existence at halt time tells the system to also cut UPS output. Step 8.FINALDELAY: a short grace period after the last warning before the command fires.
Start it:
sudo systemctl enable --now nut-monitor
Step 6: Decide When to Give Up
By default, upsmon shuts down when the UPS reports OB LB, meaning on battery and low. That is one reasonable policy and it is the simplest, but it hands the decision to the UPS firmware, and on budget units the low-battery threshold can fire with very little runtime left.
The alternative is to shut down after a fixed time on battery, which you choose. That is upssched, driven from upsmon.conf:
NOTIFYCMD /usr/sbin/upssched
NOTIFYFLAG ONBATT SYSLOG+EXEC
NOTIFYFLAG ONLINE SYSLOG+EXEC
NOTIFYFLAG LOWBATT SYSLOG+EXEC
Then in /etc/nut/upssched.conf:
CMDSCRIPT /etc/nut/upssched-cmd
PIPEFN /run/nut/upssched.pipe
LOCKFN /run/nut/upssched.lock
AT ONBATT * START-TIMER onbatt-shutdown 300
AT ONLINE * CANCEL-TIMER onbatt-shutdown
AT LOWBATT * EXECUTE onbatt-shutdown
That starts a five-minute timer when the power drops, cancels it if mains returns, and fires immediately if the battery goes low first. The handler in /etc/nut/upssched-cmd:
#!/bin/sh
case $1 in
onbatt-shutdown)
logger -t upssched-cmd "On battery too long, shutting down"
/usr/sbin/upsmon -c fsd
;;
esac
Make it executable:
sudo chmod +x /etc/nut/upssched-cmd
Five minutes is a starting point, not a recommendation. The right number is: how long does a clean shutdown of everything on this host actually take, plus a margin, and does the UPS have that much runtime at your measured load. Time your own shutdown and work backwards. A host running a dozen guests needs a longer window than a single mini PC, and the battery.runtime value from upsc tells you what you have to spend.
Step 7: Secondary Nodes
If a second machine is plugged into the same UPS, it needs to know when the power is going. It does not need its own USB connection.
On the primary, switch /etc/nut/nut.conf to:
MODE=netserver
And let upsd listen on the LAN in /etc/nut/upsd.conf:
LISTEN 127.0.0.1 3493
LISTEN 192.168.1.10 3493
Add a secondary account to upsd.users on the primary:
[secondaryuser]
password = another-random-string
upsmon secondary
On the second machine, install nut-client only, set MODE=netclient, and point it at the primary:
MONITOR homelab-ups@192.168.1.10 1 secondaryuser another-random-string secondary
Restart nut-monitor there and confirm it can read the UPS:
upsc homelab-ups@192.168.1.10
Port 3493 carries credentials in a form you do not want loose on a flat network. Firewall it to the specific hosts that need it rather than the whole subnet.
Secondaries shut down first. The primary waits HOSTSYNC seconds for them to disconnect before halting itself, which is what keeps the machine holding the UPS connection alive long enough to be useful.
Step 8: Close the Power-Cycle Loop
This is the step most guides omit, and it is the one that decides whether an outage is self-healing or a morning of walking around pressing power buttons.
When upsmon shuts the host down, the UPS is still supplying power. If nothing tells it otherwise it will keep doing that until the battery is flat, and when mains comes back the UPS has nothing left to start with. The server stays off.
The fix is the POWERDOWNFLAG from Step 5. When upsmon initiates the shutdown it writes that file; late in the halt sequence the system checks for it and, if present, calls upsdrvctl shutdown to tell the UPS to cut its own output after a delay. Mains returns, the UPS powers up, the server powers up with it.
The Debian and Proxmox packages wire this up for you. Verify rather than assume:
systemctl is-enabled nut-driver-enumerator.service
ls -l /lib/systemd/system-shutdown/nutshutdown 2>/dev/null
The delays themselves live in ups.conf on units that support them:
[homelab-ups]
driver = usbhid-ups
port = auto
offdelay = 60
ondelay = 120
offdelay is how long the UPS waits before cutting output, which must be longer than the host takes to finish halting. ondelay is how long it waits before restoring power, which should be longer than offdelay so the two never race.
Make sure your BIOS is set to power on after AC loss, or none of this matters. On most mini PCs that setting is called “Restore on AC Power Loss” or “After Power Failure,” and the default is frequently “stay off.”
Step 9: Get Told About It
A shutdown you find out about the next morning is a worse outcome than one you watch happen. upsmon.conf can run a command on any state change:
NOTIFYFLAG ONBATT SYSLOG+EXEC
NOTIFYFLAG ONLINE SYSLOG+EXEC
NOTIFYFLAG LOWBATT SYSLOG+EXEC
NOTIFYFLAG SHUTDOWN SYSLOG+EXEC
Point NOTIFYCMD at a script that pushes to whatever you already run. If that is self-hosted push, the ntfy guide covers the receiving end; if it is mail, the homelab email notifications setup applies unchanged. The important detail is that the notification path must not depend on the homelab surviving, because the entire scenario is the homelab not surviving. A push service or an external mail relay clears that bar. A self-hosted mail server on the box that is shutting down does not.
Step 10: Test It Properly
Three levels, in order.
Forced shutdown drill. This exercises the real code path without touching the hardware:
sudo upsmon -c fsd
The host should shut down now. On Proxmox, watch that guests stop first rather than getting cut off. Boot it back up and read the logs:
journalctl -u nut-monitor --since "30 minutes ago"
Battery self-test. Confirms the UPS reports state changes correctly:
upscmd -u upsadmin -p a-different-random-string homelab-ups test.battery.start.quick
upsc homelab-ups@localhost ups.status
Pull the plug. Nothing else proves it. Pick a quiet evening, unplug the UPS from the wall, and watch the whole sequence: status flips to OB, notification arrives, timer runs, guests stop, host halts, UPS cuts output. Plug it back in and confirm everything comes back without you touching a keyboard.
Do this once a year. Batteries die quietly, and a UPS with a dead battery reports the same reassuring OL it always did right up until the moment it matters. battery.runtime dropping over time is the early warning, which is worth graphing if you already run monitoring.
What Usually Goes Wrong
| Symptom | Cause |
|---|---|
ups.status never leaves OL | Wrong driver, or a data cable that is charge-only |
Driver not connected from upsc | upsd started before the driver attached; restart nut-server |
| Permission denied on the USB device | udev rules applied after install; reconnect the cable |
| Host halts but never powers back on | POWERDOWNFLAG path missing, or BIOS set to stay off after AC loss |
| Secondary never shuts down | upsd not listening on the LAN address, or port 3493 firewalled |
| Guests get cut off mid-write | Shutdown window shorter than the guests need; raise the timer or the per-guest timeout |
What This Actually Buys You
A working NUT setup does not extend your runtime by a single second. What it does is convert an uncontrolled power loss into a controlled one, which is the difference between a filesystem that mounts on the next boot and one that needs a recovery session. On a host running databases, ZFS, or anything mid-write, that is the whole value proposition of owning the UPS in the first place.
The configuration above is maybe an hour of work. The test at the end is the part that counts, and it is the part that gets skipped, which is why so many homelabs own a UPS that has never once done its job.