How to monitor systemd timers
To monitor a systemd timer, ping a monitoring URL from the service it starts: ExecStartPre pings when the run begins and ExecStopPost pings when it ends, with the exit status. Cronomonitor alerts you when the service fails or the timer doesn't fire.
Why monitor it
systemd records a failed unit in the journal and in systemctl --failed, but doesn't tell anyone. A timer that was never enabled, or was disabled during maintenance and not turned back on, fails even more quietly.
1. Create a check
Sign up for Cronomonitor, create a project and add a check named after the job. Give it the job's schedule, here the cron expression 0 3 * * * (every day at 3am, UTC), and a grace period that covers how long the job normally takes.
The check gives you a unique ping URL. Every snippet below is also on the check's page in the dashboard, already filled in with that URL and schedule.
2. Add the ping to your job
Then run: sudo systemctl daemon-reload && sudo systemctl enable --now nightly-backup.timer
# /etc/systemd/system/nightly-backup.service
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStartPre=-/usr/bin/curl -fsS -m 10 --retry 3 -o /dev/null https://cronomonitor.com/api/ping/<your-check-id>/start
ExecStart=/path/to/your-job.sh
# EXIT_STATUS is a number, or a signal name if the job was killed; anything but a number reports a failure.
ExecStopPost=/bin/sh -c 'case "$$EXIT_STATUS" in ""|*[!0-9]*) s=fail ;; *) s=$$EXIT_STATUS ;; esac; curl -fsS -m 10 --retry 3 -o /dev/null https://cronomonitor.com/api/ping/<your-check-id>/$$s'
# /etc/systemd/system/nightly-backup.timer
[Unit]
Description=Run Nightly backup on schedule
[Timer]
OnCalendar=*-*-* 03:00:00
[Install]
WantedBy=timers.targetHow it works
ExecStopPost runs after the service stops, whether it succeeded or failed, and systemd gives it the result in EXIT_STATUS. The one-liner sends that number as the exit code, or /fail if the service was killed by a signal. The leading - on ExecStartPre means a failed start ping never stops the job.
Cronomonitor translates the check's cron schedule into an OnCalendar value. You can check any OnCalendar expression with systemd-analyze calendar "<expression>", which prints when it next fires.
Catch up after downtime with Persistent=true
If the machine is off when the timer is due, the run is skipped. Adding Persistent=true to the [Timer] section runs it once at the next boot instead. Either way, the check tells you a run was missed.
Spread the load
RandomizedDelaySec in the [Timer] section starts each run at a random point within the delay, which helps when many machines run the same timer. Add the delay to the check's grace period so a late start isn't reported as missed.
3. Test it
Run the job once by hand, or use Send a test ping on the check. The check turns green as soon as the first ping arrives, and from then on Cronomonitor expects the next one on schedule.
To check where alerts go, use Send test alert on the check. To see a real failure alert, make the job exit with an error once.
If the check says the job didn't run
systemctl list-timers --all shows when each timer last fired and when it fires next.
systemctl status <name>.service shows the last result; journalctl -u <name>.service shows its output.
If the timer isn't listed, enable it with sudo systemctl enable --now <name>.timer.