Monitoring UPS Battery Health on a Raspberry Pi with Network UPS Tools (NUT)
This site's guide to powering a Raspberry Pi right covers UPS HATs from the hardware side — wiring a battery backup board so a power cut triggers a clean shutdown instead of a corrupted SD card. That guide stops once the shutdown script runs. This one picks up where it leaves off: using Network UPS Tools (NUT) to actually monitor battery health, runtime estimates, and charge status over time, and to let other machines on your network react to power events too, not just the Pi the UPS is physically attached to.
What NUT Actually Adds Over a Basic Safe-Shutdown Script
A UPS HAT's typical safe-shutdown setup watches one GPIO pin or I2C register for a "power lost" signal and runs a shutdown command — functional, but it tells you nothing about the battery itself. NUT is a proper UPS monitoring daemon: it polls the UPS (or UPS HAT, via a compatible driver) for actual telemetry — battery charge percentage, estimated runtime remaining, input voltage, load percentage — and exposes that data both locally and over the network to any other machine running a NUT client. That second part matters more than it sounds: if your Pi is the one physically wired to the battery but you've got other Pis, a NAS, or a print server on the same UPS circuit (or sharing power during an outage), NUT lets all of them see the same battery status and shut down in a coordinated sequence rather than each guessing independently.
Driver Compatibility: The Part That Trips People Up
NUT was built around traditional USB/serial UPS units (APC, CyberPower, Eaton) long before Raspberry Pi UPS HATs existed, and its driver list reflects that history. Most Pi UPS HATs don't speak NUT's native protocols directly — they expose battery data over I2C instead. Getting NUT working with a HAT-style battery backup typically means one of two paths: using a NUT driver specifically written for your HAT's chipset if the manufacturer or community has published one (check your HAT's documentation and GitHub before assuming you need to write your own), or running a small bridge script that reads the HAT's I2C registers and feeds the values into NUT's generic "dummy-ups" driver as if they came from a real serial UPS. The second path is more work to set up but works with essentially any I2C-based HAT, since you're writing the translation layer yourself rather than waiting for one to exist.
Setup Walkthrough
- Install NUT (sudo apt install nut on Raspberry Pi OS) and identify which driver fits your hardware — check nut-scanner output if you have a traditional USB UPS, or go straight to the dummy-ups + bridge-script path for an I2C HAT.
- Configure /etc/nut/ups.conf with your UPS's driver and connection parameters, then /etc/nut/upsd.conf and /etc/nut/upsd.users to set up the daemon and at least one monitoring user with password authentication.
- Set MODE=netserver in /etc/nut/nut.conf if you want other machines on the network to poll this Pi's UPS status — the default standalone mode only serves data locally.
- On any additional Pi or server that should see the same UPS status, install the NUT client tools only (not the full server) and point upsmon at the server Pi's IP address and the credentials you set up.
- Test with upsc your-ups-name@localhost (or the server's IP from a client machine) and confirm battery charge, runtime, and status fields are populating with real numbers, not placeholder zeros from a misconfigured driver.
Alerting and Dashboards
Raw upsc output is useful for debugging but not something you want to check manually every day. If you're already running the self-hosted monitoring stack this site's Grafana and InfluxDB-based weather station guide sets up, NUT's variables (battery charge, runtime, load) can be scraped into the same time-series database with a small polling script, giving you a historical graph of battery health and discharge cycles over months — genuinely useful for catching a UPS battery that's degrading well before it fails you during an actual outage. At minimum, configure upsmon's notification hooks to send an alert (even just a local log entry, or push through a service your shop already uses) when battery charge drops below a threshold you set, rather than finding out the battery was weak only after a shutdown script failed to complete in time.
Practical Notes
NUT's shutdown sequencing is only as good as the runtime estimate it's working from, and battery runtime estimates on small HATs degrade in accuracy as the battery ages — don't trust a "30 minutes remaining" estimate from a three-year-old battery pack the way you would from a fresh one. Periodically running an intentional discharge test (unplug mains, time how long the Pi actually stays up under real load) and comparing it against what NUT estimated is the only reliable way to catch a battery that's quietly degrading before it leaves you with less runway than you think during a real outage.
This is the piece most Pi UPS HAT guides skip entirely, because basic safe-shutdown is genuinely all most single-Pi setups need. The moment you're running more than one always-on Pi, or care about battery health over a HAT's multi-year lifespan rather than just surviving one outage, NUT turns a UPS HAT from "a thing that saves my SD card" into "a monitored piece of infrastructure."