Set Up a systemd Alert on Failure for Every Service
Your nightly backup stopped running three weeks ago. Nothing told you. You found out on the day you needed it.
Cron mails you a job's output,
but systemd has no built-in way to do the same. People have asked for one since 2019.
Until then, a failed service or timer just sits there in red until someone runs systemctl status.
FailNote: free for one server
Alerts that tell you what broke and why, without editing a single unit file.
Set it up yourself with OnFailure=
systemd can start another unit whenever a unit enters the failed state, using OnFailure=. The trick is a top-level drop-in, which adds that setting to every service at once. This takes about ten minutes.
1. Write the alert script
Save this as /usr/local/bin/notify-failure. Put your own address in the last line.
#!/bin/bash
# Usage: notify-failure <unit>
unit="$1"
host="$(hostname)"
{
echo "$unit failed on $host"
echo
systemctl status --full --no-pager "$unit"
echo
journalctl -u "$unit" -n 40 --no-pager
} | mail -s "[$host] $unit failed" [email protected] Make it executable with sudo chmod 755 /usr/local/bin/notify-failure.
This needs the mail command and a server that can send email. Prefer Slack? Swap the
mail line for a webhook call:
curl -fsS -m 10 -H 'Content-Type: application/json' \
-d "{\"text\": \"$unit failed on $host\"}" \
https://hooks.slack.com/services/YOUR/WEBHOOK/URL 2. Add a template service that runs it
Create /etc/systemd/system/[email protected]:
[Unit]
Description=Send a failure alert for %i
[Service]
Type=oneshot
ExecStart=/usr/local/bin/notify-failure %i 3. Hook it into every service
Create /etc/systemd/system/service.d/10-notify-failure.conf:
[Unit]
OnFailure=notify-failure@%N.service %N is the failed unit's name, so a failed backup.service starts
[email protected]. Top-level drop-ins like service.d/ arrived in
systemd 244. Check yours with
systemctl --version.
4. Stop the alert service from alerting on itself
The drop-in also lands on notify-failure@ itself. Mask it there, as the
systemd.unit manual shows, then reload:
sudo mkdir -p /etc/systemd/system/[email protected]
sudo ln -s /dev/null /etc/systemd/system/[email protected]/10-notify-failure.conf
sudo systemctl daemon-reload 5. Test it with a service that always fails
printf '[Service]\nExecStart=/bin/false\n' | sudo tee /etc/systemd/system/alert-test.service
sudo systemctl daemon-reload
sudo systemctl start alert-test.service
systemctl status [email protected]
You should get an alert within a few seconds. If not, the status output shows what the script hit. Clean up with
sudo rm /etc/systemd/system/alert-test.service, then sudo systemctl daemon-reload and
sudo systemctl reset-failed alert-test.service.
Where the do-it-yourself setup falls short
- Email often can't leave the server. AWS limits port 25 and Google Cloud blocks it, so you also need a mail relay.
- A dead server sends nothing. If the machine hangs or loses its network, the script never runs.
- Timers that never fire don't fail. A disabled or broken timer just stops, so
OnFailure=never triggers. - Restart loops are noisy or silent. With
Restart=, you may get an alert on every attempt or none, depending on RestartMode= and your systemd version. - Older servers need per-unit edits. Below systemd 244, you add
OnFailure=to each unit by hand. - The alert says what, not why. You still read raw logs at 2 a.m. to find the cause.
How FailNote closes those gaps
FailNote is a small alert agent for Linux servers. You run one install command, and every service and timer on the machine is covered. It sends alerts through our servers, so blocked mail ports don't matter.
curl -fsSL https://app.itadminutils.com/install.sh | sudo sh -s -- YOUR_KEY Your dashboard shows the command with your key filled in. The agent is one shell script you can read before you run it.
- Every failure explained. Alerts use the same checks as our systemd failure analyzer, so you see the likely cause and the next command to run.
- Silence counts too. If a server or a timer stops checking in, you hear about that as well.
- One alert per incident. A crash loop sends one message and a recovery note, not fifty pings.
- Your channels. Email, Slack, Discord, Microsoft Teams or any webhook.
- Old and new systemd. It works the same on servers that predate top-level drop-ins.
Here's what a failure alert looks like:
FAILED: backup.service on web-01
Result: exit-code (exited, status=203/EXEC)
Cause: systemd could not execute the configured command (Permission denied)
Check: file /opt/backup/run.sh && namei -l /opt/backup/run.sh
Log: backup.service: Failed at step EXEC spawning /opt/backup/run.sh: Permission denied That one comes up a lot. Our guide to status 203/EXEC errors walks through every cause.
What the agent sends
Each minute it sends the hostname, OS name, systemd version and the names of any failed units. When a unit fails, it also sends
that unit's systemctl status and its last 40 log lines. Set FAILNOTE_LOG_LINES=0 to keep logs on the server.
Pricing
Free for one server, with every feature. Pro covers up to 10 servers for $9 a month, and you can cancel anytime.
Start FailNote free
Enter your email. We'll send a sign-in link, and your dashboard has the install command.
Already have an account? Sign in. See our privacy policy and terms.