Server — UPS

Posted on 6 2026

A UPS without software is just a battery with a beeper. It will keep your server running through a short outage, but it has no way to tell the server that the clock is running, and the server has no way to shut itself down cleanly before the battery gives out. When the battery eventually dies, the server loses power mid-write, the filesystem gets corrupted, and you spend the next hour with fsck trying to recover. The UPS bought you time and then wasted it.

The software side of this is NUT: Network UPS Tools. It is the standard open source solution for UPS monitoring on Linux, it supports almost every APC model over USB, and it is what February runs to manage the APC connected to it. This article covers the installation, configuration, and the notification setup that tells you when something has actually gone wrong.

How NUT works

NUT has three components that work together:

The driver talks to the UPS hardware directly, in this case over USB. For APC devices, the driver is usbhid-ups.

The server (upsd) reads data from the driver and makes it available to monitoring clients over a simple network protocol. In standalone mode it listens on localhost only.

The monitor (upsmon) connects to the server, watches the UPS state, and takes action when things change. It is upsmon that triggers the shutdown when the battery gets critical.

In a standalone setup, all three run on the same machine. That is the right setup for February.

Installation

sudo apt install nut

NUT creates its configuration files in /etc/nut/. The directory is owned by the nut group and the files need to be readable only by NUT processes, so be careful with permissions when editing them.

Verify the APC is visible before touching any configuration:

lsusb | grep -i apc

You should see something like American Power Conversion in the output. If nothing appears, the USB cable is not connected or the device is not powered on.

Configuration

There are four files to edit. Work through them in order.

nut.conf

Sets the operating mode. For a single machine connected directly to the UPS:

MODE=standalone

ups.conf

Tells NUT which driver to use and how to find the device. For an APC over USB:

[apc]
    desc = "APC UPS"
    driver = usbhid-ups
    port = auto

The name in brackets (apc) is what you will use to refer to this UPS throughout the rest of the configuration. port = auto lets NUT find the USB device automatically, which works reliably for a single connected UPS.

Start the driver to confirm it can communicate with the hardware:

sudo upsdrvctl start

If the driver starts without errors, the hardware side is working.

upsd.users

Defines the credentials that upsmon uses to connect to upsd. Create one user for local monitoring:

[upsmon_local]
    password = your-password-here
    upsmon primary

upsmon.conf

The main monitoring configuration. This is the file that controls what happens when the UPS state changes:

MONITOR apc@localhost 1 upsmon_local your-password-here primary

MINSUPPLIES 1
SHUTDOWNCMD "/sbin/shutdown -h +0"
NOTIFYCMD /usr/sbin/upssched
POLLFREQ 2
POLLFREQALERT 1
HOSTSYNC 15
DEADTIME 15
POWERDOWNFLAG /etc/killpower

NOTIFYMSG ONLINE    "UPS %s: power restored"
NOTIFYMSG ONBATT    "UPS %s: running on battery"
NOTIFYMSG LOWBATT   "UPS %s: battery low, shutdown imminent"
NOTIFYMSG FSD       "UPS %s: forced shutdown in progress"
NOTIFYMSG COMMOK    "UPS %s: communications restored"
NOTIFYMSG COMMBAD   "UPS %s: communications lost"
NOTIFYMSG SHUTDOWN  "Shutting down now"
NOTIFYMSG REPLBATT  "UPS %s: battery needs replacing"

NOTIFYFLAG ONLINE   SYSLOG+EXEC
NOTIFYFLAG ONBATT   SYSLOG+EXEC
NOTIFYFLAG LOWBATT  SYSLOG+EXEC
NOTIFYFLAG FSD      SYSLOG+EXEC
NOTIFYFLAG COMMOK   SYSLOG
NOTIFYFLAG COMMBAD  SYSLOG+EXEC
NOTIFYFLAG SHUTDOWN SYSLOG
NOTIFYFLAG REPLBATT SYSLOG+EXEC

A few things worth noting here. POLLFREQ 2 and POLLFREQALERT 1 mean NUT checks the UPS state every two seconds normally, and every second when the UPS is on battery. SHUTDOWNCMD is the command that actually shuts the system down when battery is critical. NOTIFYCMD points to upssched, which handles the notification side and is configured separately.

The EXEC flag on a NOTIFYFLAG line is what causes upssched to be called for that event. Without it, NUT only logs to syslog.


Notifications with upssched

upssched is a small helper that sits between upsmon and your notification script. Its job is to handle timing: rather than firing off an alert the instant the UPS switches to battery (which might be a half-second blip), you can tell it to wait 30 seconds before doing anything. If power returns in that window, the timer is cancelled and nothing happens.

upssched.conf

CMDSCRIPT /etc/nut/upssched-cmd
PIPEFN /run/nut/upssched.pipe
LOCKFN /run/nut/upssched.lock

AT ONBATT  * START-TIMER onbatt 30
AT ONLINE  * CANCEL-TIMER onbatt online
AT LOWBATT * EXECUTE lowbatt
AT REPLBATT * EXECUTE replbatt
AT COMMBAD * START-TIMER commbad 30
AT COMMOK  * CANCEL-TIMER commbad commok

The 30-second timer on ONBATT filters out brief power interruptions. LOWBATT fires immediately because at that point there is no time to wait. COMMBAD gets the same 30-second treatment, since a momentary USB hiccup should not generate an alert.

upssched-cmd

This is the script that upssched calls when a timer fires. Create it at /etc/nut/upssched-cmd and make it executable:

#!/bin/bash

NOTIFY_TO="you@example.com"
NOTIFY_FROM="february@your-domain.com"

notify() {
    echo "$2" | mail -s "$1" "$NOTIFY_TO"
    logger -t upssched-cmd "$1"
}

case "$1" in
    onbatt)
        notify "UPS: February on battery"                "The APC UPS has been on battery for 30 seconds. Check for a power outage."
        ;;
    online)
        notify "UPS: February power restored"                "Mains power has been restored. February is back on line power."
        ;;
    lowbatt)
        notify "UPS: February battery critical"                "Battery is critically low. February is shutting down now."
        ;;
    replbatt)
        notify "UPS: February battery needs replacing"                "The UPS battery health is degraded. Schedule a replacement."
        ;;
    commbad)
        notify "UPS: February lost contact with UPS"                "NUT can no longer communicate with the APC UPS. Check the USB connection."
        ;;
    commok)
        notify "UPS: February UPS contact restored"                "NUT has re-established communication with the APC UPS."
        ;;
    *)
        logger -t upssched-cmd "Unrecognised event: $1"
        ;;
esac
sudo chmod +x /etc/nut/upssched-cmd

The notification method here is mail, which requires a working mail transfer agent on February. If you are not running one, the simplest swap is a curl call to whatever webhook or notification service you use. The case structure stays the same; only the delivery mechanism changes.

Starting the services

sudo systemctl enable nut-server nut-client
sudo systemctl start nut-server nut-client

Check that NUT can see the UPS and read its state:

upsc apc@localhost

This should return a list of UPS variables including ups.status, which should read OL (on line) if everything is working. If you see OL CHRG, the UPS is on mains power and the battery is charging.

Check the service logs if anything is not starting:

journalctl -u nut-server -u nut-client --no-pager -n 50

Testing

Do not skip this step. A UPS setup that has never been tested under real conditions is a UPS setup you cannot rely on.

Test the notifications by temporarily setting a very short ONBATT timer in upssched.conf, then briefly unplugging the UPS from mains. You should see the ONBATT event in the logs and receive the notification email. Plug it back in and confirm you receive the ONLINE notification.

Test the shutdown by running:

sudo upsmon -c fsd

This triggers a forced shutdown through NUT exactly as it would happen on low battery. February will shut down. Schedule this for a maintenance window and confirm the shutdown is clean.

Do both tests before you trust this setup to protect anything important.

What you now have

When mains power fails, February knows within two seconds. If power does not return within 30 seconds, you get an email. If the battery reaches a critical level before power returns, NUT shuts February down gracefully before the battery dies, and you get another email. When power is restored, you get an email about that too.

The SD card stays intact. The filesystem stays consistent. The only thing you lose is the uptime.