Notifications for failing Systemd services

Posted on July 16, 2021 by Adrian Wyssmann ‐ 3 min read

When systemd services fail you usually don't really notice it unless it's a critical service. However there may be situations where it would be nice that you are mare aware of failing services.

Motivation

On my machine I have setup backup jobs using papanito.borg. This role sets up a systemd service and a systemd timers which executes the backup regularly and unless I explicitly check with systemctl status automatic-backup-clawfinger-local I will not know if the backup was successful or not. This may be fatal if I need a recent backup but the most successful backup happened month ago. So I would like to get a notification in case of a failure.

Possibilities

As the backup solution above executes a script, I might implement the notification in the script. But my focus here is a more generic approach which is suitable for other services as well. Systemd actually offers different possibilities for things to happen in case of a failure:

  • ExecStopPost=: Additional commands that are executed after the service is stopped - to invoke commands when a service failed to start up correctly and is shut down again.
  • FailureAction=: Configure the action to take when the service enters a failed state.
  • OnFailure=: A space-separated list of one or more units that are activated when this unit enters the “failed” state.

FailureAction and ExecStopPost

FailureAction seems the better choice for my initial goal - notify in case of failures. In conjunction with notify-send one could send notifications directly to the desktop:

[Service]
Type=simple
ExecStart=/bin/sh /opt/borg_backups/automatic-backup-clawfinger-local.sh
User=aedu
Group=aedu
FailureAction=/usr/bin/notify-send --app-name="%N" "Failure"

Unfortunately that does not work, and as I suspected, it is related to the missing graphical user session

notify-send communicates over the dbus session bus but cron jobs are not part of any graphical user session;

So following the post above, I have adjusted FailureAction as follows

FailureAction="DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/1000/bus /usr/bin/notify-send --app-name="%N" 'Failure'"

However, systemd does not like it - or well I don’t know how to properly write the command in the .service-file

Jul 15 19:56:50 archlinux systemd[1]: /etc/systemd/system/automatic-backup-clawfinger-local.service:10: Neither a valid executable name nor an absolute path: DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

So I’ve created a script in /usr/local/bin/systemd-notify.sh with the following content, assuming the logged in user is 1000:

#!/usr/bin/env bash
DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus notify-send --app-name=$1 $1@$(hostname) "Failed"

And the .service file updated accordingly:

FailureAction=/usr/local/bin/systemd-notify.sh "%N"

As this still does not work, I use ExecStopPost instead, which seems to work fine.

Desktop Notification

Ultimately I enhanced the script a bit so that one can also specify the user id as well as add more detailed information to the notification. Have a look at https://github.com/papanito/shell-scripts/blob/master/scripts/scripts/systemd-notify.sh

OnFailure

A nice alternative is OnFailure, which requires an additional systemd job of type oneshot, let’s call it systemd-desktop-notifier.service

[Unit]
Description=Desktop Notification of %i

[Service]
Type=oneshot
ExecStart=/usr/local/bin/systemd-notify.sh %i
User=nobody
Group=systemd-journal

You can then specify OnFailure in the .service-file of the service you want to get notifications as follows in the [Unit] section

OnFailure=systemd-desktop-notifier@%N.service

Using the %N specifier here and the %i specifier in the systemd-desktop-notifier.service, are used to properly set the notification message. The interesting thing using OnFailure is, that you can provide a command separated list which allows you to send notifications in multiple ways.

For instance, I also have a googlechat notifications - useful if you are not sitting on your computer and you get a notification. Thus I would have a seconde service systemd-googlechat-notifier.service

[Unit]
Description=Desktop Notification of %i

[Service]
Type=oneshot
ExecStart=/usr/local/bin/systemd-googlechat-notify.sh %i
User=nobody
Group=systemd-journal

Which calls systemd-googlechat-notify.sh that contains the following code

#!/usr/bin/env bash
curl --data "{'text':'*$1@$(hostname)*\nFailed'}" --header 'Content-Type: application/json; charset=UTF-8' --request POST  https://chat.googleapis.com/v1/spaces/xxxxxxxxxxxxxxxxxx

You may check systemd-googlechat-notify.sh which shows an enhanced script which you can use, if you are using googlechat.