Skip to content

Linux server with UPS

Introduction

Having a server, it's a good practice to protect it from power supply failures. Solution for such case is to use UPS. Occasionally power down can last longer than UPS battery capacity and then it's recommended to gracefully shutdown server. In order to accomplish this, UPS needs to have some kind of connection with the server (common one is USB) and on the server dedicated software must be installed. Some UPS manufacturers have their own software, but if we have a less common model or an older UPS, than a good solution is to use open source software such as NUT (Network UPS Tools). Big advantage of NUT software is that we can use one software to manage UPS devices from different manufacturers. NUT software is in standard Debian repositories so it's easy to install and can be used on Proxmox hypervisor which is based on Debian. This way, we can have nice homelab hypervisor connected to UPS for protection against power supply issues. In this article I will be using UPS Socomec NPE-0650.

OS used: Debian 12
Software used: nut 2.8.0
Hardware used: UPS Socomec NPE-0650

Source

This article is based on information from Techno Tim blog post and NUT offcial documentation.

UPS model to choose

If you don't have UPS and want to buy one I would check NUT Hardware compatibility list and pick model with support level based on publicly available protocol or higher.

Packages installation

Install nut metapackage which will install nut-server and nut-client. NUT has client/server architecture where clients access UPS hardware through the server and is notified about power status changes.

# apt install nut

UPS scan

First we need to find UPS device connected to our host:

# lsusb
(...)
Bus 001 Device 002: ID 0665:5161 Cypress Semiconductor USB to Serial
(...)

# nut-scanner -U
Scanning USB bus.
[nutdev1]
        driver = "nutdrv_qx"
        port = "auto"
        vendorid = "0665"
        productid = "5161"
        product = "USB to Serial"
        vendor = "INNO TECH"
        bus = "001"

nut-scanner -U - USB scan

Configuration

UPS configuration

Create UPS configuration at the end of /etc/nut/ups.conf file:

# vim /etc/nut/ups.conf
/etc/nut/ups.conf
(...)
[npe650]
    driver = nutdrv_qx
    port = auto
    desc = "Socomec NPE-0650"
    vendorid = "0665"
    productid = "5161"

  • [npe650] - your own name for UPS
  • driver, port, vendorid, productid - data copied from results of nut-scanner command
  • desc - your description

Mode configuration

Now we need to choose which parts of NUT are started. Should it work only for localhost, work as a server or work only as a client. We will choose option for localhost only which is called standalone.

# vim /etc/nut/nut.conf
/etc/nut/nut.conf
(...)
MODE=standalone
(...)

Configuration of data server daemon upsd

By default upsd will only listen to localhost port 3493/TCP. If you want to connect to NUT server from other hosts this must be configured here. It is used for example in configuration where we have one dedicated machine like Raspberry Pi with many UPS devices connected and other hosts connect to get information about UPS statuses. In this tutorial we will use default configuration with allowed connection only from localhost. Add folowing line in the section with LISTEN:

# vim /etc/nut/upsd.conf
/etc/nut/upsd.conf
(...)
LISTEN 127.0.0.1 3493
(...)

Create NUT user

Create a upsd user for upsmon. Edit upsd.users and create a new section:

# vim /etc/nut/upsd.users
/etc/nut/upsd.users
[monuser]
        password = mypass
        upsmon primary

  • [monuser] - set your user name
  • password - set your password, if you use some of special characters there can be problems with password, first configuration of nut run with simple password then change it to desired
  • upsmon - adds the necessary actions for upsmon process to work. This is either set to primary or secondary. If host is connected to UPS directly and can manage it using a NUT driver then upsmon is the primary.

Check upsd.users file permissions and ownership because it contains password. Should be as follows:

# ls -l /etc/nut/upsd.users
-rw-r----- 1 root nut 2319 Jan 25  2023 /etc/nut/upsd.users

Configuration of NUT client upsmon

Create MONITOR line with name of UPS to which we connect:

# vim /etc/nut/upsmon.conf
/etc/nut/upsmon.conf
(...)
MONITOR npe650@localhost 1 monuser mypass primary
(...)

  • <upsname>@<hostname> - UPS name from ups.conf to which we connect
  • 1 - power value, it is the sum of all UPS devices that supply power to the system hosting upsmon. Can be also set to 0 with secondary which means monitoring only and does not shut down when the UPS does.
  • monuser - username from upsd.users
  • mypass - password from upsd.users
  • primary - primary setting from upsd.users

Check upsmon.conf file permissions and ownership because it contains password (otherwise attacker knowing this password can invoke shutdown). Should be as follows:

# ls -l /etc/nut/upsmon.conf
-rw-r----- 1 root nut 19786 Jan 25  2023 /etc/nut/upsmon.conf

Run NUT

  • Reboot system to apply configuration files changes.
  • When system is up again check status of service - service should be active (running):
# systemctl status nut-monitor

There can be errors like these below but I don't think it causes any problems (here is some info in NUT GitHub issue):

fopen /run/nut/upsmon.pid: No such file or directory
Could not find PID file to see if previous upsmon instance is already running!

Checking UPS data

Check UPS data with upsc <upsname>@<hostname> command.

# upsc npe650@localhost
battery.voltage: 13.70
device.type: ups
driver.name: nutdrv_qx
driver.parameter.pollfreq: 30
driver.parameter.pollinterval: 2
driver.parameter.port: auto
driver.parameter.productid: 5161
driver.parameter.synchronous: auto
driver.parameter.vendorid: 0665
driver.version: 2.8.0
driver.version.data: Q1 0.07
driver.version.internal: 0.32
driver.version.usb: libusb-1.0.26 (API: 0x1000109)
input.frequency: 50.0
input.voltage: 226.5
input.voltage.fault: 224.4
output.voltage: 224.4
ups.beeper.status: enabled
ups.delay.shutdown: 30
ups.delay.start: 180
ups.load: 14
ups.productid: 5161
ups.status: OL
ups.temperature: 25.0
ups.type: offline / line interactive
ups.vendorid: 0665

As you can see Socomec NPE-0650 shows very little detail. Most important will be:

  • ups.delay.shutdown - interval to wait after shutdown with delay command in seconds
  • ups.delay.start - interval to wait before (re)starting the load in seconds
  • ups.load - load on UPS in %
  • ups.status - show UPS status:
    • OL - online
    • OL CHRG - charging
    • OB - on battery
    • OB LB - low battery
    • FSD - system shutdown
  • ups.temperature - UPS temperature in °C

Better UPS will have more parameters like shown below (that is why don't be to cheap on UPS):

  • battery.charge - shows current battery charge in %
  • battery.charge.low - shows battery charge in % when UPS is signaling low battery and by default upsmon will initiate shutdown procedure
  • battery.runtime - shows current runtime on battery in seconds
  • battery.runtime.low - shows predicted runtime when low battery in seconds
  • ups.timer.shutdown - time before the load will be shutdown in seconds
  • ups.timer.start - time before the load will be started in seconds

Tuning automatic shutdown

NUT will initiate shutdown when ups.status reaches state OB LB. For my Socomec UPS this left to little juice in battery to properly shutdown system. If you have similar situation you can invoke shutdown command earlier by overriding parameters for low battery state. Details for commands can be found in specific driver manual page. Here is manual for nutdrv_qx.

# vim /etc/nut/ups.conf
/etc/nut/ups.conf
[npe650]
    driver = nutdrv_qx
    port = auto
    desc = "Socomec NPE-0650"
    vendorid = "0665"
    productid = "5161"
    # override parameters
    ignorelb
    override.battery.voltage.high = 12.30
    override.battery.voltage.low = 10.60
    override.battery.charge.low = 50

  • ignorelb - NUT driver ignores a low battery condition flag that is reported by the UPS and will use following condition battery.charge < battery.charge.low
  • override.battery.voltage.high - max charged battery voltage (after 12 to 24h) that UPS shows when switching to battery
  • override.battery.voltage.low - low battery voltage when UPS automatically shutdowns (generally it's a state when UPS beeps rapidly)
  • override.battery.charge.low - percent of battery charge below which NUT will initiate shutdown

Note

override.battery.voltage.high and override.battery.voltage.low for nutdrv_qx driver will show battery.charge and battery.charge.low values. These values are estimated and are not reliable under load.

Restart NUT service for changes to take effect:

# systemctl restart nut-monitor

Test UPS

Simulating power failure

To simulate power failure call forced shutdown flag FSD. Shutdown procedure will be initiated and it is possible to time how long it takes for hosts to shutdown.

# upsmon -c fsd

Real life test

  • Watch UPS status with watch command:
# watch -d upsc npe650@localhost

-d - highlight differences between updates of watch command

  • Swich off UPS power
  • UPS will work on battery
  • Watch for file /etc/killpower. It is created by upsmon when shutdown sequence is initiated:
# watch -d ls -l /etc/killpower
  • After some time status will be low battery
  • Shutdown procedure will start
  • System will switch off
  • UPS will switch off

Info

By default when shutdown sequence is completed the UPS will switch off. It will switch on after return of mains AC power and delay time specified in ups.delay.start (for my Socomec it is 180 seconds which is 3 minutes). So don't be surprised during testing that when you switch on AC power and your UPS doesn't start immediately but after certain time.