Uninterruptible power supply
Uninterruptible power supply (UPS), provides near-instantaneous protection from input power interruptions by switching to energy stored in battery packs, supercapacitors or flywheels. The on-battery run-times of most UPSs are relatively short (only a few minutes) but sufficient to "buy time" for initiating a standby power source or properly shutting down the protected equipment. (source: Wikipedia)
Network UPS Tools (NUT) is a collection of software for managing power devices, mainly UPS units. This article describes the configuration of NUT for a simple server with a single power supply, with no local users and no additional equipment.
This article is mostly adapted from the excellent NUT ConfigExamples book, version 3.0, by Roger Price. [1]
Compatible Hardware
Compatible hardware are listed at NUT website [2]. For best results, choose a model with good driver support with battery replacement notification. Battery pack in UPS is designed to be replaced every few years. For example, Eaton Ellipse ECO 650 [3] needs a new battery every four years, under optimal operating conditions.
The end of ConfigExample book says:
Joe's server will still be allright if power drops off in the night. That 8 year old pack of battery backup will easily handle th connection lost
Components of NUT Software
One or more UPS's are attached to the attachment
daemon upsd via a UPS-specific driver
daemon.
The attachment daemon maintains an abstract image of the UPS in
memory. The attachment daemon can be queried by the upsc
command. The driver daemon talks to the hardware and the attachment
daemon. The driver daemon can be controlled by the
upsdrvctl command.
The management daemon upsmon is a client
of upsd. It runs permanently, checks the status of UPS, and react to
status changes, such as initiating a shutdown.
Configuration files in use
In this simple standalone server setup, the following configuration files are generated:
- ups.conf, declare UPS-specific driver information, power.ups.ups.* option
- upsd.conf, control access to upsd, power.ups.upsd option
- upsd.users, add user with access to upsd, power.ups.users option
- upsmon.conf, connect upsmon to upsd, power.ups.upsmon.monitor section;
- upsmon.conf, set how upsmon should react to status changes, power.ups.upsmon.settings section
- delayed UPS shutdown systemd unit, to make Restore Power on AC Return BIOS option functional, systemd.services.nut-delayed-ups-shutdown section
Declare UPS units
Corresponds to file ups.conf
power.ups = {
enable = true;
mode = "standalone";
# section: The upsd UPS declarations: ups.conf
# this UPS device is named UPS-1.
ups."UPS-1" = {
description = "Eaton Ellipse ECO 650 with 12V 7Ah lead-acid Batt";
# driver name from https://networkupstools.org/stable-hcl.html
driver = "usbhid-ups";
# usbhid-ups driver always use value "auto"
port = "auto";
directives = [
# "Restore power on AC" BIOS option needs power to be cut a few seconds to work;
# this is achieved by the offdelay and ondelay directives.
# in the last stages of system shutdown, "upsdrvctl shutdown" is called to tell UPS that
# after offdelay seconds, the UPS power must be cut, even if
# wall power returns.
# There is a danger that the system will take longer than the default 20 seconds to shut down.
# If that were to happen, the UPS shutdown would provoke a brutal system crash.
# We adjust offdelay, to solve this issue.
"offdelay = 60"
# UPS power is now cut regardless of wall power. After (ondelay minus offdelay) seconds,
# if wall power returns, turn on UPS power. The system has now been disconnected for a minimum of (ondelay minus offdelay) seconds,
# "Restore power on AC" should now power on the system.
# For reasons described above, ondelay value must be larger than offdelay value.
# We adjust ondelay, to ensure Restore power on AC option returns to Power Disconnected state.
"ondelay = 70"
# set value for battery.charge.low,
# upsmon initiate shutdown once this threshold is reached.
"lowbatt = 40"
# ignore it if the UPS reports a low battery condition
# without this, system will shutdown only when ups reports lb,
# not respecting lowbatt option
"ignorelb"
];
};
Declare upsd listening ports
Corresponds to file upsd.conf. This file declares which ports the upsd daemon will listen to.
power.ups = {
# section: The upsd daemon access control; upsd.conf
upsd = {
listen = [
{
address = "127.0.0.1";
port = 3493;
}
{
address = "::1";
port = 3493;
}
];
};
};
Declare users with access to UPS
Corresponds to file upsd.users. This file declares a virtual user (not related to /etc/passwd users) with write access to UPS. A password is also declared.
power.ups = {
# section: Users that can access upsd. The upsd daemon user
# declarations. upsd.users
users."nut-admin" = {
passwordFile = "${../resources/ups-passwd.txt}";
upsmon = "primary";
};
};
Connect upsmon to upsd
Corresponds to upsmon.conf. This file declares how upsmon should connect to upsd
power.ups = {
# section: The upsmon daemon configuration: upsmon.conf
upsmon.monitor."UPS-1" = {
system = "UPS-1@localhost";
powerValue = 1;
user = "nut-admin";
passwordFile = "${../resources/ups-passwd.txt}";
type = "primary";
};
};
Declare how upsmon should react to status changes
Corresponds to upsmon.conf. This file declares how upsmon is to handle NOTIFY events.
power.ups = {
upsmon.settings = {
# This configuration file declares how upsmon is to handle
# NOTIFY events.
# POWERDOWNFLAG and SHUTDOWNCMD is provided by NixOS default
# values
# values provided by ConfigExamples 3.0 book
NOTIFYMSG = [
[ "ONLINE" ''"UPS %s: On line power."'' ]
[ "ONBATT" ''"UPS %s: On battery."'' ]
[ "LOWBATT" ''"UPS %s: Battery is low."'' ]
[ "REPLBATT" ''"UPS %s: Battery needs to be replaced."'' ]
[ "FSD" ''"UPS %s: Forced shutdown in progress."'' ]
[ "SHUTDOWN" ''"Auto logout and shutdown proceeding."'' ]
[ "COMMOK" ''"UPS %s: Communications (re-)established."'' ]
[ "COMMBAD" ''"UPS %s: Communications lost."'' ]
[ "NOCOMM" ''"UPS %s: Not available."'' ]
[ "NOPARENT" ''"upsmon parent dead, shutdown impossible."'' ]
];
NOTIFYFLAG = [
[ "ONLINE" "SYSLOG+WALL" ]
[ "ONBATT" "SYSLOG+WALL" ]
[ "LOWBATT" "SYSLOG+WALL" ]
[ "REPLBATT" "SYSLOG+WALL" ]
[ "FSD" "SYSLOG+WALL" ]
[ "SHUTDOWN" "SYSLOG+WALL" ]
[ "COMMOK" "SYSLOG+WALL" ]
[ "COMMBAD" "SYSLOG+WALL" ]
[ "NOCOMM" "SYSLOG+WALL" ]
[ "NOPARENT" "SYSLOG+WALL" ]
];
# every RBWARNTIME seconds, upsmon will generate a replace
# battery NOTIFY event
RBWARNTIME = 216000;
# every NOCOMMWARNTIME seconds, upsmon will generate a UPS
# unreachable NOTIFY event
NOCOMMWARNTIME = 300;
# after sending SHUTDOWN NOTIFY event to warn users, upsmon
# waits FINALDELAY seconds long before executing SHUTDOWNCMD
# Some UPS's don't give much warning for low battery and will
# require a value of 0 here for aq safe shutdown.
FINALDELAY = 0;
};
};
Delay UPS Shutdown
As part of the system shutdown process, there needs to be an action to send a message to the UPS to tell it that some time later, it too will shut down. Note that the UPS does not shutdown at the same time as the system it protects. The UPS shutdown is delayed. By default the delay is 20 seconds. The absence of AC power to the protected system for a sufficient time has the effect of resetting the BIOS options, and in particular the option Restore power on AC return. This BIOS option will be needed to restart the box.
During the system shutdown, systemd service unit runs the command
upsdrvctl shutdown. This tells the UPS that it is to shut down
offdelay seconds later. The system powers down before offdelay seconds
have passed. Wall power returns before the UPS shuts down. Less than
offdelay seconds have now passed. The UPS continues it's shutdown
process.
After offdelay seconds the UPS shuts down, disconnecting it's outlets. The beeping stops. With some UPS units, there is an audible clunk. An interval of ondelay minus offdelay seconds later: After ondelay seconds the UPS turns itself on, and repowers it's outlets. The system BIOS option Restore Power on AC return has hopefully been selected and the system powers up.
There is a danger that the system will take longer than the default 20 seconds to shut down. If that were to happen, the UPS shutdown would provoke a brutal system crash. We have adjusted offdelay above, to solve this issue.
# copied from ConfigExamples 3.0 book, Appendix B.2.
systemd.services.nut-delayed-ups-shutdown = {
enable = true;
environment = config.systemd.services.upsmon.environment;
description = "Initiate delayed UPS shutdown";
before = [ "umount.target" ];
wantedBy = [ "final.target" ];
serviceConfig = {
Type = "oneshot";
# need to use '-u root', or else permission denied
ExecStart = ''${pkgs.nut}/bin/upsdrvctl -u root shutdown'';
# must not use slice: if used, upsdrvctl will not run as a late
# shutdown service
# Slice = "";
};
unitConfig = {
ConditionPathExists = config.power.ups.upsmon.settings.POWERDOWNFLAG;
DefaultDependencies = "no";
};
};