Recipe · cron / systemd

Get notified when a cron job or systemd service fails

Wrap a scheduled job so failure — and only failure — reaches your phone, without cron's noisy default of emailing every run.

Cron’s default is to email every run’s output to a local mail spool nobody reads. The fix is the same either way: check the exit code, and only notify on failure.

Cron

Wrap the job in a small script rather than editing the crontab line itself — easier to test and to reuse:

#!/usr/bin/env bash
set -euo pipefail

if ! /usr/local/bin/nightly-backup.sh; then
  curl -sS -X POST "https://<host>/events/<username>/Backups" \
    -H "Content-Type: application/json" \
    -d '{
      "title": "Nightly backup failed",
      "message": "'"$(hostname)"' — see /var/log/backup.log",
      "priority": "High",
      "nature": "error"
    }'
  exit 1
fi

Point cron at the wrapper, not the underlying command:

0 3 * * * /usr/local/bin/backup-with-alert.sh

systemd

A oneshot service can use OnFailure= to trigger a second unit only when the first one fails — no wrapper script needed:

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup
OnFailure=notify-paperplane@%n.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/nightly-backup.sh
# /etc/systemd/system/notify-paperplane@.service
[Unit]
Description=Notify Paperplane that %i failed

[Service]
Type=oneshot
ExecStart=/usr/bin/curl -sS -X POST "https://<host>/events/<username>/Backups" \
  -H "Content-Type: application/json" \
  -d '{"title":"systemd unit failed","message":"%i","priority":"High","nature":"error"}'

%i and %n expand to the failed unit’s name, so one notify-paperplane@ template covers every service you attach it to.