Overview
UPS Power connects WolfStack to your existing Network UPS Tools (NUT) setup and adds the thing NUT alone can’t express: a staged, workload-aware wind-down while you’re running on battery. Instead of one all-or-nothing shutdown timer, you decide what happens at each battery level — for example:
- at ≤ 60% battery → gracefully stop VMs and containers
- at ≤ 40% battery → stop file-sharing services
- at ≤ 20% battery → shut the host down
Between the trigger points you get a live UPS faceplate on the node’s UPS Power page and on the home dashboard, alerts through every notification channel you’ve configured, and WolfFunctions events so you can hook your own logic into an outage.
WolfStack never touches your NUT configuration
Operators run upsd, upsmon and the UPS drivers themselves — that’s deliberate. WolfStack only reads UPS state through the NUT client (upsc), so it layers on top of any working NUT install, local or remote, without ever rewriting a config file you own. If you already have a NUT server on your network (a NAS, a Raspberry Pi, another host), pointing WolfStack at it is a one-field setup.
Requirements
- A working NUT server (
upsd) that can see your UPS — on the WolfStack node itself or anywhere reachable on the network (port 3493 by default). - The NUT client tools (
upsc) on the WolfStack node. If they’re missing, the UPS Power page offers a one-click Install NUT client tools button — WolfStack knows the right package per distro (nut-clienton Debian/Ubuntu/Fedora,nuton Arch/Alpine/openSUSE).
No NUT server yet? NUT’s own documentation covers driver setup; once upsc yourups@host answers, WolfStack is ready to use it.
Setup
Open a server in the tree and go to UPS Power. Configuration is one card:
- UPS to monitor — the NUT target. Use
myupsfor a UPS served by a localupsd, ormyups@192.168.1.10(optionallymyups@host:port) for a remote NUT server. If a localupsdis running, its UPS names are offered as suggestions. - Poll interval — how often WolfStack reads the UPS, 5–300 seconds (default 15).
- Enabled — the staged-shutdown engine only acts when this is on and a target is set.
The Test connection button does an immediate read of the target and shows the UPS status and charge — use it before enabling anything. Test failures are reported with the actual reason (unknown UPS name, server unreachable, NUT server misconfiguration) rather than a raw client error dump.
Each node runs its own engine against its own UPS target — on battery, every host winds down its own workloads. Settings live in /etc/wolfstack/ups.json.
The faceplate
The status card renders a live UPS front panel, the same way WolfRouter draws a router:
- Status LEDs — ONLINE / ON BATTERY / LOW BATTERY (or FAULT) plus an INPUT lamp.
- LCD panel — charge percentage, estimated runtime, and load, colour-coded green / amber / red. When the UPS can’t be read it shows NO COMMS; before configuration it shows SETUP.
- Battery bar — a 10-segment charge gauge.
- Switched outlet groups — one outlet group per configured stage, green while its workloads are powered and red once that stage has fired — the load-shedding metaphor real smart-UPS hardware uses.
The same faceplate is available as a home-dashboard widget (one per server, add as many as you have UPSes) — see Home Dashboard.
Staged shutdown
Stages are rows of “when charge drops to X%, do these actions”. A one-click Add recommended stages gives you the 60/40/20 layout above; edit freely. Available actions, executed in this order within a stage:
- Stop VMs — gracefully stops every WolfStack-managed VM through the same per-platform path as the UI’s stop button (Proxmox
qm, libvirtvirsh, or native), then sweeps for any still-running unmanaged qm/libvirt guests. - Stop containers — stops all running Docker containers, Proxmox
pctcontainers and classic LXC containers. - Stop shares — stops Samba and the NFS server (both unit-name spellings across distros). Network filesystems this host merely mounts are left alone.
- Shut down host —
systemctl poweroff(with ashutdown -h nowfallback). The action log is flushed to disk before this runs.
Engine rules — designed to never fire by accident
- On-battery only. Stages are only considered while live UPS data says the UPS is on battery. Stale or unreadable data never triggers actions — a dead NUT server can’t shut your fleet down.
- Once per outage. Each stage fires once and latches; latches clear when mains power returns. Editing the config mid-outage can’t re-fire a stage that already ran.
- Gentle before harsh. If a fast drain crosses two thresholds in one poll, the higher (gentler) stage still runs before the lower one — VMs are stopped before the host powers off, always.
- Boot-while-on-battery safe. If the host starts during an outage, all currently-due stages fire in order.
Action log that survives the shutdown
Everything the engine does — going on battery, each stage firing, every stop command’s outcome, power restored — is written to a persistent action log at /var/lib/wolfstack/ups-log.json, flushed on every entry. The host may be about to power itself off, so the post-mortem has to survive the shutdown the engine causes. After the outage, the UPS Power page shows exactly what happened and when.
Alerts and automation
- Alerts — going on battery, power restored, and every stage trigger raise WolfStack alerts, delivered through whatever channels you’ve set up on the Alerting & Notifications page (Discord, Slack, Telegram, email, …).
- WolfFunctions events — three trigger events fire for serverless functions:
ups_on_battery,ups_online, andups_stage_fired(the payload names the stage percentage and its actions). Subscribe a WolfFunction to run your own wind-down logic — pause a job queue, notify an external system, flush an application cache — alongside the built-in actions.
Troubleshooting
- “Unknown error” from the test button — the NUT server replied with an error the NUT client doesn’t recognise. Check that the target is
name@hostwith the exact UPS name the server exposes, and that the hostname configured on the NUT server itself is correct. (Seen in the wild with a NUT server whose own hostname setting was wrong.) - TLS chatter in older versions — the NUT client opportunistically negotiates TLS and prints informational lines about it; they’re not the failure. Current WolfStack surfaces only the actual error reason.
- No
battery.charge— some drivers don’t report a charge percentage. Stages are %-keyed, so they can’t fire on such a UPS; WolfStack logs this once per outage so it isn’t silent. - UniFi UPS hardware — the UniFi UPS Tower’s built-in NUT server is non-standard (it can require a login for reads, which plain
upscdoesn’t support, and sends some non-standard protocol replies). Make sure its login requirement is disabled and use the exact UPS name from the UniFi console.