How to Configure an Orderly Shutdown on a Raspberry Pi

Once you have NUT-tools configured on your Raspberry to monitor your UPS, the next step is configuring the slaves. The slaves are your servers and other computers that are plugged into the UPS. Looking at my UPS stats on the NUT web server pages, it looks like I’ve got about 16 minutes of runtime under the current load. This post will lead you through configuring your server to shutdown after 10 minutes. Honestly, this whole place is on standby generator power, so the lights might go out for 15-20 seconds during the switchover. The UPS will handle that, easy! But if the generator fails, I’ve got to make sure that someone does a “shutdown -h now” on my LINUX servers. And that someone? NUT!

The Story So Far…

So far, we’ve got nut-tools running on a Raspberry Pi 3. It’s got a USB connection to the UPS and my webserver is plugged in to that UPS. IF the power is out for 10 minutes or more, it’s not coming back on anytime soon, so I need NUT to send a command to shutdown the server.

The last Raspberry we configured was the “master”. It’s got a USB connection to the UPS and watches for the UPS switching between mains and battery power. Now, it’s time to configure the “slaves” or the computers that hear that Raspberry screaming that the lights are out!

How to Install a NUT client

First, install the nut client:

sudo apt install nut-client

To get the slave talking to the master, you’ll have to edit two files: nut.conf and upsmon.conf. The nut.conf file is used to put the software into slave mode. Edit / add the following line to nut.conf. (I just put it right at the end so it’s easy to find.):

MODE=none
change to 
MODE=netclient

Next, we’re going to add a line to upsmon.conf. You’ll need the name of the UPS, the username, and the password that we configured on the master.

MONITOR <system> <powervalue> <username> <password>  ("master"|"slave")
change to 
MONITOR servers@192.168.1.193 1 monuser secret slave

This time the MONITOR is a little different. It’s watching the Raspberry that’s running NUT, not the UPS directly. ups is the name of the UPS that my servers are plugged into. The IP address is that of the Raspberry. 1 is the powermode. Just leave this at 1. monuser is the user that I configured. secret is the password. slave is the mode. Restart:

systemctl restart nut-monitor.service

Now, check your connection to the master. If you’re copying my config, the test looks like this. upsc is the command. “servers” is the name of the UPS. “ups” is the name of the raspberry that has the USB connection to the actual UPS:

root@server1:~# upsc servers@ups
 Init SSL without certificate database
 battery.charge: 100
 battery.charge.low: 10
 battery.charge.warning: 20
 battery.mfr.date: CPS
 battery.runtime: 1008
 battery.runtime.low: 300
 battery.type: PbAcid
 battery.voltage: 27.2
 battery.voltage.nominal: 24
 device.mfr: CPS
 device.model: CP1350AVRLCDa
 device.serial: CTJJY2003442
 device.type: ups
 driver.name: usbhid-ups
 driver.parameter.pollfreq: 30
 driver.parameter.pollinterval: 15
 driver.parameter.port: auto
 driver.parameter.product: .50A.
 driver.parameter.synchronous: no
 driver.version: 2.7.4
 driver.version.data: CyberPower HID 0.4
 driver.version.internal: 0.41
 input.voltage: 124.0
 input.voltage.nominal: 120
 output.voltage: 140.0
 ups.beeper.status: enabled
 ups.delay.shutdown: 20
 ups.delay.start: 30
 ups.load: 35
 ups.mfr: CPS
 ups.model: CP1350AVRLCDa
 ups.productid: 0501
 ups.realpower.nominal: 815
 ups.serial: CTJJY2003442
 ups.status: OL
 ups.test.result: No test initiated
 ups.timer.shutdown: -60
 ups.timer.start: -60
 ups.vendorid: 0764
 root@server1:~#

What to Do When the Lights Go Out

Now, we need to instruct nut what to do when it hears from the master that the UPS battery is draining! Edit upsmon.conf and search for SHUTDOWNCMD. That should lead you to the definition of how to run the shutdown command. Run “which shutdown” on your server to see where the command is. On my system it’s in: /usr/sbin/shutdown so I need to edit the line in upsmon.conf to look like this:

SHUTDOWNCMD "/usr/sbin/shutdown -h now"

Next, we’ll edit upssched.conf. This is where you can configure the UPS statuses and what nut should do about them. The format for each line is: AT notifytype upsname command The commands themselves are configured in /usr/bin/upssched-cmd The timers take an argument that is the number of seconds. After that number of seconds, the command is sent to upssched-cmd for immediate execution. The CMDSCRIPT line must be first! PIPEFN and LOCKFN are files that get created by the system. You just have to specify their names and locations. PIPEFN is the pipe facility that allows upssched to talk to the script. Next, you have all of your messages coming from the monitor (named “*”) and what to do about them. The lower case word is the argument that gets sent to the command processor, upssched-cmd.

CMDSCRIPT /usr/bin/upssched-cmd

PIPEFN /etc/nut/upssched/nut.pipe
LOCKFN /etc/nut/upssched/nut.lock

AT ONBATT * START-TIMER onbatt 20
AT ONLINE * CANCEL-TIMER onbatt
AT ONBATT * START-TIMER earlyshutdown 300
AT ONLINE * CANCEL-TIMER earlyshutdown
AT LOWBATT * START-TIMER shutdowncritical 30
AT ONLINE * CANCEL-TIMER shutdowncritical
AT COMMBAD * START-TIMER upsgone 30
AT COMMOK * CANCEL-TIMER upsgone

Note the change to the path for upssched-cmd. Don’t get tripped up by the similarity of the names /usr/bin/upssched-cmd and /etc/nut/upssched.conf!

Here’s my /usr/bin/upssched-cmd:

#!/bin/sh
 case $1 in
       onbatt)
          logger -t upssched-cmd "The UPS has been on battery for awhile
          ;;
       earlyshutdown)
          logger -t upssched-cmd "UPS on battery too long, forced shutdown"
          /usr/sbin/upsmon -c fsd
          ;;
       shutdowncritical)
          logger -t upssched-cmd "UPS on battery critical, forced shutdown"
          /usr/sbin/upsmon -c fsd
          ;;
       upsgone)
          logger -t upssched-cmd "The UPS has been gone for awhile"
          ;;
       *)
          logger -t upssched-cmd "Unrecognized command: $1"
          ;;
 esac

Check what’s happening here. Most of these commands call upsmon. upsmon is the client process that is responsible for the most important part of UPS monitoring—shutting down the system when the power goes out. Once again, you’ll want to check the path (with “which”) of the location of this all too important player. Otherwise you can put whatever you like in here: commands to email you, run other processes, or whatever you need.

upsmon.conf is extensively documented within. You can read what needs to be configured. Here’s what I ended up using after tinkering with it for a couple of hours…

MONITOR printer@192.168.1.193 1 monuser secret slave
MINSUPPLIES 1
SHUTDOWNCMD "/usr/sbin/shutdown -h now"
NOTIFYCMD /usr/sbin/upssched
POLLFREQ 5
POLLFREQALERT 5
HOSTSYNC 15
DEADTIME 15
POWERDOWNFLAG /etc/killpower
NOTIFYFLAG ONLINE       SYSLOG+WALL+EXEC
NOTIFYFLAG ONBATT       SYSLOG+WALL+EXEC
NOTIFYFLAG LOWBATT      SYSLOG+WALL+EXEC
NOTIFYFLAG FSD          SYSLOG+WALL+EXEC
NOTIFYFLAG COMMOK       SYSLOG+WALL+EXEC
NOTIFYFLAG COMMBAD      SYSLOG+WALL+EXEC
NOTIFYFLAG SHUTDOWN     SYSLOG+WALL+EXEC
NOTIFYFLAG REPLBATT     SYSLOG+WALL+EXEC
NOTIFYFLAG NOCOMM       SYSLOG+WALL+EXEC
NOTIFYFLAG NOPARENT     SYSLOG+WALL+EXEC
RBWARNTIME 43200
NOCOMMWARNTIME 300
FINALDELAY 5

Feeling Brave?

OK. Time for the big test. Restart upsmon and pull the plug on the UPS! You should see a couple of wall messages. The UPS should be beeping. Depending on how you sent the timers in upsmon, your server should shut down any minute now!

16 thoughts on “How to Configure an Orderly Shutdown on a Raspberry Pi

  1. Hi, can I install both the master and the slave instance on the rpi if the rpi is the server i’d like to be shut down in a properly manner?

      1. How can nut be configured in nut.conf and upsmon.conf as master and slave at the same time?
        Following your HowTo, my nut is now configured as master and works fine including cgi, but I’m curious how to use the “shutdown mechanics” for the *same* rpi …

  2. Great article once again. I am interested in possibly sending an sms text to my phone when any of my UPSs go on battery, and possibly when they return to line power. Just curious what your approach might be? I found some good ideas in this forum Q: https://unix.stackexchange.com/questions/82895/possible-to-get-sms-text-message-notification-when-process-ends-or-is-killed

    Also, just an idea for you (would love your opinion). To separate some of the NUT log msgs out from syslog, I added the following script file to /etc/rsyslog.d/0-nut.conf… part of me wonders if this might be another way to filter for the powers alerts and send an SMS msg but, I think your NUT-specific approach documented here is ideal.

    #
    # NUT logging
    #
    # Include USB msgs since montoring UPS via only USB
    if $msg contains “USB” or $msg contains “nut-” or $msg contains “UPS” then {
    action(type=”omfile” file=”/var/log/nut.log”)
    } else {
    if $syslogtag contains “ups” or $syslogtag contains “nut” then
    action(type=”omfile” file=”/var/log/nut.log”)
    }

    Thx again for the great article.

  3. Just curious… let’s say I’m in an area that suffers from short power interruptions here and there, and I just want to page myself immediately when the UPS goes on battery, and immediately when it goes back on line power. Would my /etc/nut/upssched.conf look something like… ?

    AT ONBATT * START-TIMER onbatterypower 0
    AT ONLINE * CANCEL-TIMER onlinepower

    …or would you use some different NUT commands?

  4. OK, I am a bit confused here. Sorry, I am a bit new to Linux.

    Does the upsmon -c fsd command shut down the UPS, or does it shutdown upsmon?

    When does the Shutdown command for the RPi get executed?

    My desire would be to send a shutdown command to the ups with a delay on it, then send a push shutdown message to hub controller, and then shut down the RPI before the UPS shuts down. It looks like your script in upssched shuts down the ups at 300 seconds. when does the RPi Shutdown? It has to happen befor the UPS shuts down.

    What am I missing here?

    1. upsmon -c FSD does a forced shutdown by calling the command specified by SHUTDOWNCMD. It also tells slave upsmon instances that the final shutdown is underway and they shutdown the systems that they are configured to control.

      1. Thanks for the reply. OK So I can forget about shutting down the UPS. But to send shutdown commands to other devices attached to the same UPS before shutting down the RPi I would have to write some sort of script or shell file to send the two or three different commands to the other home automation controllers, and then the shutdown to the RPi? Does SHUTDOWNCMD allow you to execute a script or .sh file?

  5. I have followed the instructions here and in the manual, but the timer part of the NUT setup does not work. I am running on Raspberry Pi.. In /etc/nut/upsmon.conf, I put this:
    NOTIFYCMD /usr/sbin/upssched

    I have set “SYSLOG+WALL+EXEC” for all notifyfflag options. EG:

    NOTIFYFLAG ONLINE SYSLOG+WALL+EXEC
    It is an exact replica of the one on this site.

    I did confirm that “/usr/sbin/upssched” exists and has execute flag

    In /etc/nut/upssched.conf

    I have placed a lot of timers in ‘/etc/nut/upssched.conf’ trying to get one of them to work.

    AT ONBATT * START-TIMER onbatt 20
    AT ONLINE * CANCEL-TIMER onbatt
    AT ONBATT * START-TIMER earlyshutdown 600
    AT ONLINE * CANCEL-TIMER earlyshutdown
    AT ONBATT * START-TIMER nineminstoearlyshutdown 570
    AT ONLINE * CANCEL-TIMER nineminstoearlyshutdown
    AT ONBATT * START-TIMER eightminstoearlyshutdown 480
    AT ONLINE * CANCEL-TIMER eightminstoearlyshutdown
    AT ONBATT * START-TIMER sixminstoearlyshutdown 360
    AT ONLINE * CANCEL-TIMER sixminstoearlyshutdown
    AT ONBATT * START-TIMER fourminstoearlyshutdown 240
    AT ONLINE * CANCEL-TIMER fourminstoearlyshutdown
    AT ONBATT * START-TIMER twominstoearlyshutdown 120
    AT ONLINE * CANCEL-TIMER twominstoearlyshutdown
    AT LOWBATT * START-TIMER shutdowncritical 30
    AT ONLINE * CANCEL-TIMER shutdowncritical
    AT COMMBAD * START-TIMER upsgone 30
    AT COMMOK * CANCEL-TIMER upsgone

    I then placed if conditions as below into the ‘/usr/bin/upssched-cmd’

    #! /bin/sh
    case $1 in
    onbatt)
    logger -t upssched-cmd “The UPS has been on battery for awhile
    ;;
    earlyshutdown)
    logger -t upssched-cmd “UPS on battery too long, early shutdown”
    wall ” UPS on battery too long, early shutdown ”
    /usr/sbin/upsmon -c fsd
    ;;
    nineminstoearlyshutdown)
    logger -t upssched-cmd “UPS on battery 9.5 minutes till forced shutdown”
    wall “UPS on battery, 9.5 minutes till forced shutdown”
    ;;
    eightminstoearlyshutdown)
    logger -t upssched-cmd “UPS on battery 8 minutes till forced shutdown”
    wall “UPS on battery, 8 minutes till forced shutdown”
    ;;
    sixminstoearlyshutdown)
    logger -t upssched-cmd “UPS on battery, 6 minutes till forced shutdown”
    wall “UPS on battery, 6 minutes till forced shutdown”
    ;;
    fourminstoearlyshutdown)
    logger -t upssched-cmd “UPS on battery, 4 minutes till forced shutdown”
    wall “UPS on battery, 4 minutes till forced shutdown”
    ;;
    twominstoearlyshutdown)
    logger -t upssched-cmd “UPS on battery, 2 minutes till forced shutdown”
    wall “UPS on battery, 2 minutes till forced shutdown”
    ;;
    shutdowncritical)
    logger -t upssched-cmd “UPS on battery critical, forced shutdown”
    wall ” UPS on battery critical, forced shutdown ”
    ;;
    upsgone)
    logger -t upssched-cmd “The UPS has been gone for awhile”
    wall ” The UPS has been gone for awhile ”
    ;;
    *)
    logger -t upssched-cmd “Unrecognized command: $1”
    ;;
    esac

    ‘/usr/bin/upssched-cmd’ is executable according to thee file flags

  6. OK, that comment was completely unreadable, so I will try again. I have folloed the steps on this site, and the only thing that does not work is the timers. There is zero evidence that a timer is initiated or elapsed and code triggered. Is there something in particular that I should look at? I know for sure that the SYSLOG+WALL are firing when a UPS event is triggered. I have specified +EXEC. NOTIIFYCOMD points to a file that exists, and has code, and is executable. It is an expanded version of the one on this site, trying to have multiple triggers that might work.

    I have a lot of AT ONBATT that star timers, anywhere from 10 seconds to 10 minutes. The path with the .pipe and .lock files exists.

    I do believe that I understand what is required for setup thanks to your post here, and also reading the documentation, but something is not quite right, and the timers do not work at all. Anything I should focus on?

    Thanks

  7. Hello. Thank you for the instructions. I have most of this working, with the exception of the timed events. I am watching “tail /var/log/syslog”, and I see that the timers appear to start, and I can see them triggering. But in syslog, I am seeing error code 2.

    Sep 19 00:29:34 pihole1 upssched[13626]: exec_cmd(/usr/bin/upssched-cmd twominstoearlyshutdown) returned 2

    What would I hav
    e doone wrong to get erroor code 2, and how do I go about solving it? Thanks

  8. Quick heads up that this line:

    logger -t upssched-cmd “The UPS has been on battery for awhile

    should have a double quote at the end. Thanks for the instructions, I have got the NUT server set up and doing what I need.

  9. I have one question. Above you use an asterisk (*) to denote “…messages coming from the monitor (named “*”)…”. So if I name my UPS as UPS1 do I than replace the asterisks where they occur with UPS1 or just leave it as an asterisk?

  10. Attempting to generate an alert on battery power, and I see in syslog where upsmon recognizes the UPS is on battery power, but I don’t see my script get kicked off.

    upssched.conf:
    CMDSCRIPT /etc/nut/upsTeamsAlert.py
    PIPEFN /etc/nut/upssched/nut.pipe
    LOCKFN /etc/nut/upssched/nut.lock
    AT ONBATT * START-TIMER onbatt 10

    I know the script works on it’s own, but it’s not being called from upsmon. Any suggestions on what to look for?

  11. I have followed all instructions to the tee.. changing the username and passwords as needed, but I keep getting an error preventing the slave from connecting to the master.

    bhogg@network:~ $ sudo upsc servers@192.168.25.202
    Error: Connection failure: Connection refused

    the master is at 192.168.25.202

    bhogg@network is the slave pi

    any thoughts or suggestions?

Leave a Reply to David Jacobson Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.