One UPS, Whole Homelab: Safe Auto-Shutdown with NUT

Use Network UPS Tools (NUT) so a single UPS can cleanly shut down every machine in your homelab when the power fails — one server, many network clients, step by step.

On this page
  1. How NUT protects a multi-machine homelab
  2. Task 1: Install and enable NUT on the primary
  3. Task 2: Point NUT at your UPS
  4. Task 3: Open the network and add a monitor account
  5. Task 4: Tell upsmon when and how to shut down
  6. Task 5: Protect the other machines (the secondaries)
  7. The shutdown sequence, in order
  8. Common gotchas
  9. 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.

One UPS protects the whole clusterOne data cable. One machine manages the UPS; the rest listen over the network.UPSbattery230V mains inNode 1 — NUT serverrole: primaryusbhid-ups → upsd → upsmonthe one machine wired to the UPSLAN switchNode 2NUT client · secondaryupsmon onlyNASNUT client · secondaryupsmon onlyUSB datapower to every machineNUT status · TCP 3493powerUSB data (primary only)UPS status over the network

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 like ups.status: OL (on line) or OB (on battery).
  • upsd is the network server. It publishes those variables on TCP port 3493 so other machines — and you — can read them.
  • upsmon is 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.

What NUT does and doesn't need

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.

First: make these values your own

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.

1Install the NUT server package3 min

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:

Primary — install NUT

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):

Primary — check the version

upsc -V
2Detect the UPS over USB2 min

With the UPS plugged into the wall and its USB cable into this machine, ask NUT to find it:

Primary — scan for the UPS

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.

Only one program can own the UPS

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.

3Set the NUT mode to netserver1 min

/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:

Primary — /etc/nut/nut.conf

# 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

1Define the UPS in ups.conf3 min

Tell the driver which UPS to talk to. Use the block nut-scanner printed, or write it by hand. For a USB unit:

Primary — /etc/nut/ups.conf

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.

2Start the driver and confirm it reads the battery2 min

Restart the server so the driver connects, then read the live values back with upsc:

Primary — start and verify

systemctl restart nut-server
upsc myups

You should see a dump of variables. The two that matter most:

Primary — the key status line

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.

1Listen on the LAN in upsd.conf2 min

Add a LISTEN line for the primary’s own LAN address (keep localhost too, so local tools still work):

Primary — /etc/nut/upsd.conf

cat > /etc/nut/upsd.conf <<'EOF'
LISTEN 127.0.0.1 3493
LISTEN 10.0.0.10 3493
EOF
Use distinct addresses, not 0.0.0.0 plus 127.0.0.1

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.

2Create the monitor account in upsd.users2 min

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:

Primary — /etc/nut/upsd.users

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.

1Configure the primary's upsmon3 min
Primary — /etc/nut/upsmon.conf

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.

2Start monitoring on the primary1 min
Primary — start upsmon

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.

1Install NUT and set netclient mode2 min

On a secondary you only need the client package:

Secondary — install and set mode

apt update && apt install -y nut-client
echo "MODE=netclient" > /etc/nut/nut.conf
2Point its upsmon at the primary2 min

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:

Secondary — /etc/nut/upsmon.conf

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:

Secondary — read the UPS over the network

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:

What happens when the battery runs lowOLOn linemains is fineOBOn batterymains lost — waitingOB LBLow batterycritical — go now1. Secondaries shut downclients power off firstprimary waits for them (HOSTSYNC)2. Primary shuts down lastthen signals the UPS to cut power

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.

Test it before you trust it — carefully

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

  • upsd listens on localhost by default. The single most common “my clients can’t connect” cause. Add the LISTEN line (Task 3) and restart nut-server.
  • 0.0.0.0 and 127.0.0.1 together bind only localhost. Overlapping listen addresses silently collapse to one. Use distinct addresses or a lone 0.0.0.0.
  • Config files are root:nut mode 640. They hold passwords; if you hand-edit and the perms drift, upsd may refuse to read them.
  • Only one UPS tool at a time. apcupsd and NUT both grab the USB device; disable one.
  • POWERDOWNFLAG is mandatory in primary mode. upsmon has 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:


Recommended hardware for this setup:

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.