NUT on my Pi, so my servers don't die

NUT Pi in Rack

A few weeks ago, power went out for the first time in my studio space, and that meant all my servers just had power cut with no safe shutdown.

Handling power outages is never a top priority... until it's the only priority! And by then it's usually too late! Luckily for me, no data was lost, and my servers all came back up safely.

This week the power company emailed and said they'd be cutting power for maintenance next week, but they don't have an exact time. So it's even more excuse to finally set up NUT on a Pi!

NUT, short for Network UPS Tools, is an open source tool you run on an old Pi or whatever old computer, and it monitors UPSes like the Lowell Power UPS in my main rack. NUT clients, then, can monitor the UPS through the Pi, and safely shut down before the battery is depleted.

Video

I have a full video going over my setup, along with a walkthrough of my end-to-end UPS power fail test, over on my YouTube channel. You can also watch it below (or scroll past and read through the instructions on the blog!):

Setting up a Pi as a NUT server

First, flash Pi OS (I used Pi OS Bookworm 'Lite') to a microSD card and plug that into your Pi. Boot up the Pi, make sure you can log into it over SSH, and then begin the process of setting up NUT.

For my Pi, I mounted it in the back of my rack with this Raspberry Pi Rack Mount on Printables (printed in ASA), and I just powered it off a USB-C power adapter, which is plugged into the PDU behind my rack UPS.

Since NUT doesn't require much in the way of resources, you can run it on most any Pi model—the Pi I used was an old Compute Module 4 with 1 GB of RAM I had laying around, mounted in a BigTreeTech Pi4B CM to Pi 4 adapter board.

Configuring a UPS for NUT

Before doing anything else, install NUT with sudo apt install -y nut. This will install nut-client, nut-server, and other UPS tools you will use later

Then, assuming you have your UPS plugged into a USB port on your Raspberry Pi, run nut-scanner, and see what it finds:

$ sudo nut-scanner -U
Scanning USB bus.
[nutdev1]
    driver = "nutdrv_qx"
    port = "auto"
    vendorid = "0665"
    productid = "5161"
    product = "UPS"
    bus = "001"

You can take those values, and use them in your UPS configuration (assuming you just have one UPS connected). Edit NUT's ups.conf file with sudo nano /etc/nut/ups.conf and add your UPS configuration. For example:

[server-room-rack]
    driver = nutdrv_qx
    product = UPS
    desc = "Server Room Lowell Power Rack UPS"
    port = auto
    vendorid = 0665
    productid = 5161
    bus = 001

The name of the UPS (in my case, server-room-rack) should be ASCII characters with no spaces or special characters besides -.

Save that file and close it.

Note: If you can't connect to your UPS, or the scanner doesn't find it, read through the NUT documentation on UPS drivers to see if you might be missing something. You don't have to use a USB connection, though it's the most common across all the modern UPSes I've used.

Setting up NUT server

To make the Pi run as a NUT Server, and not just for local monitoring, edit the upsd.conf file with sudo nano /etc/nut/upsd.conf and add a LISTEN directive:

LISTEN 0.0.0.0 3493

Save that file and close it.

You'll also need to define a list of NUT users who will be able to manage the UPS either locally or over the network. Edit upsd.users with sudo nano /etc/nut/upsd.users and add something like the following:

[admin]
    password = ADMIN_PASSWORD_HERE
    actions = set
    actions = fsd
    instcmds = all

[observer]
    password = OBSERVER_PASSWORD_HERE
    upsmon secondary

The admin user will have full access to do anything, including immediately send out a shutdown command (fsd) to all connected systems. So... don't use that account for all the clients, it's just for admin tasks!

The observer account is what I use on all my NUT clients to connect back and monitor the main UPS.

Configure the UPS monitor on the NUT Pi by editing upsmon.conf, using sudo nano /etc/nut/upsmon.conf:

# Make sure you use your actual admin password...
MONITOR server-room-rack@localhost 1 admin ADMIN_PASSWORD_HERE primary

# You might also want to configure FINALDELAY and set it to a period long enough
# for your servers to all shut down, prior to the primary node shutting down and
# triggering the UPS to switch off its load, e.g. for 3 minutes:
FINALDELAY 180

Save and close the file, then edit nut.conf (with sudo nano /etc/nut/nut.conf) and change the MODE from none (default) to netserver:

MODE=netserver

Restart the NUT server and make sure it is enabled at system boot:

sudo systemctl restart nut-server
sudo systemctl enable nut-server
sudo systemctl restart nut-monitor
sudo systemctl enable nut-monitor

Confirm NUT works

Check if you can see all your UPS details with upsc [ups-name-here]:

$ upsc server-room-rack
Init SSL without certificate database
battery.charge: 24
battery.energysave: no
battery.packs: 1
battery.protection: yes
battery.runtime: 0
battery.voltage: 50.60
battery.voltage.nominal: 48.0
device.model: LILVX2K0
device.type: ups
driver.name: nutdrv_qx
...

If you want to see a fancy web UI, a few of those exist (like nut_webgui), but in my case, I have Home Assistant, and monitor all the vitals in there. Home Assistant has an official NUT integration that automatically identified the NUT Pi over the network after I added the Integration to my HA install. All I had to do was add the observer username and password.

Then I created a simple card with the most important UPS statistics in one of my HA dashboards:

NUT - Home Assistant UPS card

The YAML for that card, in case you want to replicate it, is:

type: vertical-stack
title: Server Room Rack UPS
cards:
  - type: history-graph
    entities:
      - name: Status
        entity: sensor.server_room_rack_status
    hours_to_show: 4
  - type: gauge
    entity: sensor.server_room_rack_battery_charge
    name: Battery Charge
    severity:
      green: 50
      yellow: 20
      red: 0

Just to show what the Docker-base nut_webgui looks like, though, I launched an instance on my Mac (localhost) using the command:

docker run \
  -e UPSD_ADDR=10.0.2.10 \
  -e UPSD_USER=observer \
  -e UPSD_PASS=PASSWORD_HERE \
  -p 9000:9000 \
  ghcr.io/superioone/nut_webgui:latest

And it has a pretty complete dashboard with stats for all connected UPSes. Clicking on one brings up a fancy details page with every metric and configurable option available:

NUT Web Monitor running in Docker

Setting up a NUT Client on other nodes

The NUT Server will shut down last, after sending an fsd notice out to all connected clients. But your other servers need to be configured with nut-client before they will connect!

On each of your servers you want to have shut down cleanly with your primary UPS, set up the client and configure it to connect back:

  1. Install nut-client: sudo apt install nut-client
  2. Verify connection to server: upsc server-room-rack@IP_ADDRESS (where IP_ADDRESS is the IP of the NUT Pi server)
  3. Configure NUT's UPS monitor for client: sudo nano /etc/nut/upsmon.conf and add a MONITOR line:
    • MONITOR server-room-rack@IP_ADDRESS 1 observer PASSWORD slave (where IP_ADDRESS is the IP of the NUT Pi server, and PASSWORD is the observer password)
  4. Edit /etc/nut/nut.conf and set MODE=netclient (change from default none)
  5. Restart and enable nut-client:
    • sudo systemctl restart nut-client
    • sudo systemctl enable nut-client

Each server where you have nut-client running should be tracking the primary NUT server, and should shut down if it sends out an fsd notice.

Monitoring NUT server and clients

If you want more verbose logs, you can set the NUT_DEBUG_LEVEL environment variable when restarting the NUT services, but by default, it will log important notifications and things like the UPS going from 'online' to 'battery'.

# On server
journalctl -f -u nut-server

# On client
journalctl -f -u nut-monitor

Managing the connected UPS

On the NUT Pi server, you can use upscmd to manage the connected UPS, using the admin user, for example:

# List commands supported on this UPS
upscmd -l server-room-rack

# Run a quick battery test (requires password)
upscmd -u admin server-room-rack test.battery.start.quick

Debian 12 upsmon bug

When I did this last command, I unintentionally triggered a bug with the current version of nut-client on Debian 12... if a UPS self-test is run, the 'CAL' flag is set (calibration), and while it's set, any critical UPS battery alerts are ignored! (See the original NUT bug report.)

There's a bug report in Debian currently: CAL flag in UPSMON never cleared, shutdown procedure will not be triggered, and one workaround is to restart nut-client (sudo systemctl restart nut-client) on a cron job, maybe every hour, or if you know your UPS self-test schedule, immediately following.

Testing NUT

There are three different layers of testing you can do, to verify NUT is running correctly.

NOTE: These options could result in data loss! Make sure you're not doing any critical activities on the systems while you're testing your power setup...

1 - NUT Debug / test mode

I... never tried this, but apparently you can do a 'soft test' following the instructions in Dan Langille's blog: nut – testing the shutdown mechanism

2 - Live test of NUT without unplugging NAS

You can trigger the fsd event manually on the NUT Pi server with:

upsmon -c fsd

That will immediately emulate the condition of the UPS status OB LB (On Battery / Low Battery), which tells all connected systems to run their shutdown command.

3 - Live end-to-end test with UPS on battery

Unplug your UPS. Monitor the stats with upsc (like upsc server-room-rack, for mine), and validate the various parameters are correct for your NAS.

Wait a while, and keep an eye on metrics like:

$ upsc server-room-rack
battery.charge: 32
...
battery.voltage: 51.30
...
ups.load: 11
...
ups.status: OL

At some point, the UPS will have a status like OL LB or ALARM OB, and NUT should trigger shutdowns on all your connected NUT clients.

Conclusion

There are a number of options you can set on both the NUT server and clients which I've not covered in this post. The documentation is pretty dense, but readable.

The defaults are good for most use cases, but you might want to trigger an FSD earlier, before your UPS goes into 'LB' or Low Battery state. This might be useful if you have a high-power-draw server that takes 5-10 minutes to shut down. You could have it shut down when the battery's at 50% or has X minutes remaining, instead of waiting to the very end.

I'm automating my NUT setup, so it's easier to apply uniformly against all my servers, and here are the Ansible projects I'm using:

Comments

A few questions I've been seeing in YouTube comments:

What if my older UPS has a serial port (e.g. RS232) instead of USB?

You might need to figure out the right serial port driver to use with NUT (like blazer_ser), and you'll have to either us a custom GPIO to serial port adapter, or a USB to serial adapter (some are pretty flaky, I don't have particular recommendations), but it's doable.

Also see the NUT Cables documentation for guides on common UPS cable adapter pinouts.

Is it insecure having a plaintext password in the upsmon.conf file?

Yes... though NUT's documentation points out the file should be given the least privileges possible (e.g. permissions like 640 or more strict), and you should also only configure nut-clients to use an account like the observer I use above, which can only read data.

I'd still prefer there to be some more secure auth method, but a password or key in a file approach at least allows Linux to manage permissions, I guess...

What about the scenario of power being restored before all systems are shut down?

Most decent UPSes will automatically restore power after a certain period of time with wall power restored (mine, I think, was 120 seconds). And NUT will do a full shutdown cycle when it sends FSD.

NUT will send out the FSD, wait until it's timeout is reached if any servers aren't acknowledging the shutdown order, then shut itself off as it sends the UPS a 'switch off load' event.

Then the UPS is supposed to go through its own logic to restore the load once power is stable, after its own internal delay.

So, basically: all systems will power off and the load will be switched off, even if power is restored. But it's up to the UPS to re-start the load after that... There's probably some scenarios where it would get stuck in a load-off state, depending on your UPS.

How did all the servers power on automatically after power fail?

For the servers I have running on a ThirdReality Zigbee Smart Outlet, I have the Smart Outlets configured to restore the last state of power when power went out, so they automatically turn back on when power is restored.

But for the servers themselves, SBC-based servers like the Pi-NVR, which is running off a Compute Module 4, power on by default like a normal Raspberry Pi would, immediately as power is applied. The NAS is running OpenBMC on the ASPEED chip included on the server motherboard, and that takes a couple minutes to boot—then after that, I have the BIOS on the server configured to boot the server after power restore.

I have a question on this. You didn't mention any configuration on the clients when they will shutdown. This is important a) to minimize or maximize the reminding battery b) the correct behavior on the scenario of power being restored

I used the defaults on all the clients (for now at least), as it will just shut down when getting the fsd message from the primary NUT node.

In your video at the end, you mention something you called a "Rescue PDU"? What make/model is that? If you don't make a video on it, I think I might need one of those haha! Great video and very timely, just suffered a power outage yesterday that my servers were not ready for! :(

I have a similar setup with APCUPSC on a Raspberry pi 1 which shuts down and starts my ProxMox server on power loss. My project saudiqbal. github .io /Linux/APCUPSD-UPS-Server-Proxmox-Notification.html is here.

Last summer I decided I wanted to monitor a UPS I have that has no computers attached so I installed NUT on a Pi Zero 2 W. I named it "peanut" since it's so small. :-) My OS of choice is Ubuntu Server and I needed to configure a swap file in cloud-init before I could get it to successfully install. The Pi Zero 2 W is slow but fast enough for the job.

If your rack is not grounded - really earth grounded - unplugging it leaves your rack's path to ground 'floating'. Often that means the path to ground is through whatever's plugged into the rack - like network cables or other things that leave the rack. Those are generally not rated for power to ground.
To test what you're testing, you want to interrupt power using a switch that continues ground to the rack while stopping power. UPS's often have that. Else you could use a high-end power bar - one that can handle 20A. Otherwise under lower quality wiring, you could cause a ground fault or even a fire hazart and/or damage equipment.
I never post stuff - but I enjoy your work - just being careful...
G

Definitely—and actually grounding the rack is on my list of TODOs as well!

Since moving in that's been around the same priority as getting NUT set up (which is... "super important but not important enough to ever get done" apparently).

I'm moving it up on the list mostly because I'm starting to add a couple external antennas, and with RF especially, having solid grounding everywhere isn't just 'nice to have' anymore, it's extremely important! The wall rack is actually grounded separately, but not the main rack.

I cant get the upsmon to login successfully unless I add 'upsmon primary' to the admin section

root@khadas-vim1s:/etc/nut# cat upsd.users
[admin]
    password = ADMINPASS
    actions = set
    actions = fsd
    instcmds = all
    upsmon primary

[monitor]
    password = MONITOR
    upsmon secondary

I'm running Debian 12, not sure this is my error or a required tweak.

Thanks !

If anyone like me is getting an error of "Cannot load USB library (libusb-1.0.so) : file not found. USB search disabled." when trying to use nut-scanner on Ubuntu 24.04 with the included nut package of 2.8.1; its just a simple fix of some missing symlinks, as I discovered from the blog: bookstack . bluecrow . net /books/linux/page/nut-network-ups-tools (can't link due to spam protections)

Simply link these files like so:

pushd /usr/lib/x86_64-linux-gnu/
sudo ln -s libusb-1.0.so.0 libusb-1.0.so
sudo ln -s libnetsnmp.so.40 libnetsnmp.so
sudo ln -s libavahi-client.so.3 libavahi-client.so
sudo ln -s libfreeipmi.so.17 libfreeipmi.so
sudo ln -s libneon-gnutls.so.27 libneon.so
popd

Cheers, Jeff. As always your videos give me new ideas of things to do.

I thought about using a pi, but then I noticed that my TrueNAS server had a NUT service so I'm now setting that up and having home assistant monitor. I have a cyberpower 650va unit but I'd like something larger. I have a lithium phosphate battery but would need something to monitor and transfer power quickly. All the purpose built UPS like that cost $$$$.

Yeah the trouble is 'real' UPSes have some fairly expensive circuitry to provide true sine wave output, and the immediate switchover and safe battery charge/discharge circuitry to handle large, sensitive equipment...

I'm interested in testing my Ecoflow battery + inverter at some point, maybe on my at-home homelab. Would be a cheaper alternative and many people have tested and found the switching to be reliable/fast enough.

Hey Jeff

Did this last year - you reminded me to update the software on my pi3 and 5 - to monitor and maintain the fibre UPS connection downstairs and the kit in the office.

I always wanted to use a lightweight iOS app or similar to monitor and alert - I haven’t found anything that can alert though. Have you?