UPS Monitoring with NUT on Linux

Network UPS Tools (NUT) is a comprehensive open-source framework for monitoring Uninterruptible Power Supplies and triggering graceful shutdowns during power failures. This guide covers installing NUT on Linux, configuring USB and SNMP UPS drivers, setting up monitoring modes, scripting shutdown procedures, and sharing UPS status across multiple servers.

Prerequisites

  • Ubuntu 20.04/22.04 or CentOS/Rocky Linux 8+
  • A UPS with USB, serial, or SNMP connectivity
  • Root or sudo access

Install NUT

# Ubuntu/Debian
sudo apt update && sudo apt install -y nut nut-client

# CentOS/Rocky Linux
sudo dnf install -y nut

# Verify installation
upsd --version

# NUT components:
# - nut-driver: communicates with UPS hardware
# - upsd: serves UPS data over the network
# - upsmon: monitors UPS status and triggers shutdown

Identify and Configure Your UPS

# Find connected USB UPS
lsusb | grep -iE "APC|Eaton|CyberPower|Tripplite|UPS"

# Scan for all supported UPS devices
sudo nut-scanner -U   # USB scan
sudo nut-scanner -S   # SNMP scan (on network)
sudo nut-scanner -n   # USB+SNMP+Serial auto-detect

# Example output:
# [nutdev1]
#   driver = "usbhid-ups"
#   port = "auto"
#   vendorid = "051d"
#   productid = "0002"
#   product = "Back-UPS ES 700"
#   vendor = "American Power Conversion"
#   bus = "001"

# List APC driver compatibility
ls /lib/nut/  # Shows all available drivers

Configure the NUT Driver

# Configure NUT to run in standalone mode (single server, single UPS)
sudo tee /etc/nut/nut.conf > /dev/null <<'EOF'
MODE=standalone
EOF

# Configure the UPS driver
sudo tee /etc/nut/ups.conf > /dev/null <<'EOF'
[myups]
  driver = usbhid-ups
  port = auto
  desc = "APC Back-UPS ES 700"
  # For APC: use apcupsd driver instead if preferred
  
  # Optional: set UPS-specific settings
  # override.battery.charge.low = 30    # Shutdown at 30% battery
  # override.battery.runtime.low = 300  # Shutdown at 5 minutes remaining
EOF

# For a serial UPS:
# port = /dev/ttyS0 or /dev/ttyUSB0

# For SNMP UPS (e.g., network-connected APC Smart-UPS):
sudo tee /etc/nut/ups.conf > /dev/null <<'EOF'
[myups]
  driver = snmp-ups
  port = 192.168.1.50         # UPS management IP
  community = public          # SNMP community string
  snmp_version = v1
  desc = "APC Smart-UPS 1000 (SNMP)"
EOF

# Test the driver connection
sudo upsdrvctl start

# Verify it's working
sudo upsc myups@localhost
# Should show battery status, load, voltage, etc.

Configure the NUT Server (upsd)

upsd exposes UPS data to monitoring clients over the network:

# Configure upsd to listen on localhost (standalone mode)
sudo tee /etc/nut/upsd.conf > /dev/null <<'EOF'
# Listen only on localhost (single server setup)
LISTEN 127.0.0.1 3493

# For network sharing, also listen on the LAN IP:
# LISTEN 192.168.1.10 3493
LISTEN ::1 3493

# Maximum clients
MAXCONN 15
EOF

# Configure upsd user accounts (for upsmon to authenticate)
sudo tee /etc/nut/upsd.users > /dev/null <<'EOF'
[monuser]
  password = my-monitor-password
  upsmon master    # "master" = can initiate shutdown; "slave" = notification only
  
[slaveuser]
  password = slave-password
  upsmon slave     # For remote servers monitoring this UPS
EOF

sudo chmod 640 /etc/nut/upsd.users
sudo chown root:nut /etc/nut/upsd.users

# Start and enable the services
sudo systemctl enable nut-server nut-driver
sudo systemctl start nut-driver
sudo systemctl start nut-server

# Verify upsd is running
sudo systemctl status nut-server
nc -z localhost 3493 && echo "upsd is listening"

Configure the NUT Monitor (upsmon)

upsmon watches the UPS status and triggers shutdown when needed:

sudo tee /etc/nut/upsmon.conf > /dev/null <<'EOF'
# Syntax: MONITOR <ups> <power_value> <user> <password> <type>
# power_value = how many power supplies this UPS feeds (usually 1)
MONITOR myups@localhost 1 monuser my-monitor-password master

# Battery charge threshold for shutdown (0-100%)
MINSUPPLIES 1

# How low battery must be before shutdown
RBWARNTIME 43200   # Warn every 12 hours about replace battery
NOCOMMWARNTIME 300 # Warn if no comms for 5 minutes

# Shutdown command
SHUTDOWNCMD "/sbin/shutdown -h +0 'UPS battery low - shutting down'"

# Run this script when power status changes
NOTIFYCMD /etc/nut/notify.sh

# Notification events and their actions
NOTIFYFLAG ONLINE   SYSLOG+WALL+EXEC
NOTIFYFLAG ONBATT   SYSLOG+WALL+EXEC
NOTIFYFLAG LOWBATT  SYSLOG+WALL+EXEC
NOTIFYFLAG FSD      SYSLOG+WALL+EXEC
NOTIFYFLAG COMMOK   SYSLOG+WALL
NOTIFYFLAG COMMBAD  SYSLOG+WALL
NOTIFYFLAG SHUTDOWN SYSLOG+WALL+EXEC
NOTIFYFLAG REPLBATT SYSLOG+WALL
NOTIFYFLAG NOCOMM   SYSLOG+WALL

# Notify messages
NOTIFYMSG ONLINE  "Power restored on UPS %s"
NOTIFYMSG ONBATT  "UPS %s is on battery!"
NOTIFYMSG LOWBATT "UPS %s battery is low - prepare for shutdown"
NOTIFYMSG FSD     "UPS %s: forced shutdown in progress"
NOTIFYMSG SHUTDOWN "System shutting down due to UPS battery"

# Poll interval (seconds)
POLLFREQ 5
POLLFREQALERT 5    # Poll more frequently when on battery
EOF

sudo chmod 640 /etc/nut/upsmon.conf
sudo chown root:nut /etc/nut/upsmon.conf

# Enable and start upsmon
sudo systemctl enable nut-monitor
sudo systemctl start nut-monitor
sudo systemctl status nut-monitor

Network UPS Sharing

Configure one server as the NUT master and others as slaves:

On the NUT master (the server connected to the UPS via USB/serial):

# /etc/nut/nut.conf
MODE=netserver    # Serve UPS data to network clients

# /etc/nut/upsd.conf — listen on LAN IP
LISTEN 127.0.0.1 3493
LISTEN 192.168.1.10 3493   # LAN IP of the master server

# /etc/nut/upsd.users — add a slave user
[slaveuser]
  password = slave-password
  upsmon slave

# Allow firewall access
sudo ufw allow 3493/tcp comment "NUT UPS monitoring"

On each slave server:

# /etc/nut/nut.conf
MODE=netclient    # Monitor a remote NUT server

# /etc/nut/upsmon.conf
MONITOR [email protected] 1 slaveuser slave-password slave
# "slave" type: receives shutdown notification but doesn't initiate it

SHUTDOWNCMD "/sbin/shutdown -h +0 'UPS signal from master - shutting down'"

sudo systemctl enable nut-monitor
sudo systemctl start nut-monitor

Email Notifications

# Create the notification script
sudo tee /etc/nut/notify.sh > /dev/null <<'EOF'
#!/bin/bash
# Called by upsmon for NOTIFYFLAG EXEC events
# $1 is the notification message

RECIPIENT="[email protected]"
HOSTNAME=$(hostname -f)

echo "UPS Notification from $HOSTNAME:

$1

UPS Status:
$(upsc myups@localhost 2>/dev/null | grep -E 'battery|ups.status|ups.load|input')
" | mail -s "UPS Alert: $1" "$RECIPIENT"
EOF

sudo chmod +x /etc/nut/notify.sh

Test the setup:

# Check UPS status
upsc myups@localhost

# Key values to monitor:
# ups.status: OL (online), OB (on battery), LB (low battery), CHRG (charging)
# battery.charge: percentage (0-100)
# battery.runtime: estimated seconds remaining
# ups.load: percentage of UPS capacity in use
# input.voltage: incoming mains voltage

# Watch UPS status in real time
watch -n 5 "upsc myups@localhost | grep -E 'battery|status|load|voltage'"

# List all connected UPS units
upsc -l

# Simulate a power failure (brief, will restore automatically)
# Use the physical test button on the UPS, or for APC:
sudo upscmd myups@localhost test.battery.start.quick

Troubleshooting

"Cannot open /dev/usb/hiddev0" or USB permission error:

# Add the nut user to the plugdev group
sudo usermod -aG plugdev nut
sudo systemctl restart nut-driver

# Check USB device permissions
ls -la /dev/bus/usb/$(lsusb | grep -i APC | awk '{print $2"/"$4}' | tr -d ':')

# Add a udev rule if needed
sudo tee /etc/udev/rules.d/99-nut-ups.rules > /dev/null <<'EOF'
SUBSYSTEM=="usb", ATTR{idVendor}=="051d", ATTR{idProduct}=="0002", MODE="0664", GROUP="nut"
EOF
sudo udevadm control --reload-rules

upsd can't connect to driver:

# Check driver is running
sudo systemctl status nut-driver
sudo upsdrvctl start

# Check the socket file
ls /run/nut/

# View driver logs
sudo journalctl -u nut-driver -n 50

upsmon not triggering shutdown:

# Test the shutdown command manually
sudo /sbin/shutdown -h now   # Verify this works

# Check upsmon is monitoring correctly
sudo upsc myups@localhost ups.status
# OL = online power, OB = on battery

# Simulate a forced shutdown (to test):
sudo upsmon -c fsd   # Sends FSD to all monitors — use with caution

Conclusion

NUT provides a robust, vendor-agnostic framework for protecting servers against power outages. By configuring upsmon with appropriate battery and runtime thresholds, your servers will shut down gracefully before the UPS battery is exhausted. The network sharing feature is particularly valuable in rack environments where multiple servers depend on the same UPS — all slaves receive the shutdown signal from the single master that's physically connected to the UPS.