Assorted Nerdery

Setting up a UPS for a home lab

After a recent power outage at our house, I purchased a UPS to help keep the internet online the next time. In addition, we noticed that when there was an outage the power tended to come on and off multiple times before stabilizing.

I’ve been trying to buy products from brick and mortar stores rather than Amazon due to the prevalence of counterfeit products mixed in with legitimate goods. In our area, Microcenter tends to be a decent option for computer and maker related items. They offer a price match which removes the downside of purchasing in-person.

I ended up purchasing a Cyberpower LE1000DG UPS. There were other options, but this one seemed reasonable enough. When I got it home, I discovered that there was a USB port on it. This port allows a computer to monitor the status of the UPS. I thought this was pretty neat, as it would allow me to automatically cleanly power down devices selectively depending on how long the outage lasts for.

The router we use at home is a Turris Omnia. CZ.NIC, a Czech ISP created the device for use as the digital center for homes. The device has a moderately fast CPU, generous RAM and eMMC memory and allows the use of an m-SATA SSD to expand the storage available on the device. Having the high spec allows the router to share a few roles like running a Wireguard VPN, an OpenVPN VPN, print server, or NextCloud server. It even has the capability to run Linux Containers using LXD.

The software on the devices is OpenWRT based which means there is a lot of packaged software available. One of the packages available is the Network UPS Tools (NUT) package. This package consists of two parts:

  • nut_server - a daemon which connects to the UPS hardware.
  • nut_monitor - a daemon which periodically communicates with the nut_server to get the status of the hardware.

Software installation

Fortunately, OpenWRT packages the NUT software and provides a nice wiki page. I find using the command line interface more convenient than using the LuCi web interface to install and configure packages.

> opkg update

> opkg install nut nut-common nut-upsmon nut-upscmd nut-server nut-driver-usbhid-ups

For space reasons, OpenWRT splits out the drivers from the NUT software. The driver / UPS pairings are on the NUT website. For the LE1000DG UPS, the correct driver is the USB-HID one.

After installing the software, we need to setup the configuration.

config driver_global 'driver_global'
        option user 'nut'

config driver 'ups'
        option driver 'usbhid-ups'
        option port 'auto'
        option pollinterval '15'

config user
        option username 'upsuser'
        option password '1337p455w0rd'
        option upsmon 'master'

config listen_address
        option port '3493'
        option address '192.168.1.1'

config upsd 'upsd'
        option maxage '15'
        option statepath '/var/run/nut'
        option maxconn '1024'
        option runas 'nut'
config upsmon 'upsmon'
        option hostsync '15'
        option deadtime '25'
        option notifycmd '/usr/sbin/sms.sh'
        option onlinemsg 'UPS %s on line power'
        option onbattmsg 'UPS %s on battery'
        option lowbattmsg 'UPS %s battery is low'
        option fsdmsg 'UPS %s: forced shutdown in progress'
        option commokmsg 'Communications with UPS %s established'
        option commbadmsg 'Communications with UPS %s lost'
        option shutdownmsg 'Auto logout and shutdown proceeding'
        option replbattmsg 'UPS %s battery needs to be replaced'
        option nocommmsg 'UPS %s is unavailable'
        option noparentmsg 'upsmon parent process died - shutdown impossible'
        option lowbattnotify 'EXEC+SYSLOG'
        option commoknotify 'EXEC+SYSLOG'
        option commbadnotify 'EXEC+SYSLOG'
        option shutdownnotify 'EXEC+SYSLOG'
        option nocommnotify 'EXEC+SYSLOG'
        option noparentnotify 'EXEC+SYSLOG'
        list onlinenotify 'EXEC'
        list onlinenotify 'SYSLOG'
        list onbattnotify 'EXEC'
        list onbattnotify 'SYSLOG'
        list fsdnotify 'EXEC'
        list fsdnotify 'SYSLOG'
        list replbattnotify 'EXEC'
        list replbattnotify 'SYSLOG'

config master
        option upsname 'ups'
        option port '3493'
        option powervalue '1'
        option username 'upsuser'
        option password '1337p455w0rd'
        option hostname '192.168.1.1'

In the upsmon configuration section, note the “notifycmd” line. This line calls a user defined program to do something when an event happens. In my case, I’m using Flowroute’s SMS API to send me a text when the power goes out and back on.

The SMS script uses curl with the access token and API jey to send a message passed in as an argument. The text of the message is from the “onlinemsg”, “onbatterymsg” and other similar lines.

#!/usr/bin/env bash
set -euo pipefail

ACCESS="API_ACCESS_TOKEN"
KEY="API_KEY"


curl https://api.flowroute.com/v2.1/messages \
-u $ACCESS:$KEY -X POST \
-H "Content-Type: application/json" \
-d "{\"to\":\"TONUMBER\"\",\"from\":\"FROMNUMBER\", \"body\":\"$1\"}"

Replace the API_ACCESS_TOKEN, API_KEY, FROMNUMBER and TONUMBER with the appropriate values for your environment. So far this has worked quite well for me!

Now that we have communication with the UPS and notifications sorted out, the next step is to configure other computers running on the UPS to power down safely when the power goes out. The threshold for each device should depend on how critical a service is.

On my network, I have a small fileserver that I’d like to shut down when the power goes off. This machine is running a recent LTS version of Ubuntu.

$ sudo apt install nut-client

This installs a client which can communicate with the nut server running on the router. The installed configuration file (/etc/nut/nut.conf) has a lot of comments in it, but the key line is the following:

MODE=netclient

The /etc/upsmon.conf file should include the following lines:

MONITOR ups@192.168.1.1 1 upsuser 1337p455w0rd slave

SHUTDOWNCMD "/sbin/shutdown -h +0"

The “slave” designation indicates that the system should power down when the UPS level is critical. You can call out a custom shutdown script to perform some actions prior to shutting down.

Final Thoughts

This setup has been running at my house for several months now, and worked as designed during a power outage. My file server shut itself down, preserving power for my cable modem, wireless access points and my router.