GUIDE · SYSTEMD · FREE

Monitor systemd timers — success, failure, and silence

ExecStartPost pings on success. An OnFailure unit pings on failure. And when the timer stops firing entirely — masked unit, lost enablement, machine down — LastPing alerts on the silence. Three small unit files, copy-paste below. Free, no monitor cap.

systemd remembers failures. It doesn't report them.

A failed unit sits patiently in systemctl --failed, waiting for a human who isn't coming.

Failure mode 01

Failed units nobody lists

The unit fails, the state is recorded, the journal has the traceback — and unless someone runs systemctl --failed on that exact host, nobody ever learns about it.

Failure mode 02

The timer that stops firing

A unit left disabled after a rebuild, a missing Persistent=true so a powered-off window is skipped forever, a rename that broke WantedBy — the timer doesn't fail. It just stops existing.

Failure mode 03

The journal as a write-only medium

Errors land in the journal with perfect fidelity and zero reach. Journald is superb at recording what happened — and mute about telling you.


Setup: three unit files

The example monitors a nightly backup; rename to match your job. If curl lives elsewhere on your distro, check with which curl.

Create a heartbeat monitor

In the console, create a monitor matching the timer's schedule (a simple interval or a cron expression with an IANA timezone) plus a grace window, and copy the ping URL.

https://ping.lastping.dev/<your-monitor-id>

The service — ping on success

For Type=oneshot, ExecStartPost runs only after ExecStart exits successfully — so the ping means the job actually worked:

# /etc/systemd/system/backup.service [Unit] Description=Nightly backup OnFailure=backup-fail.service [Service] Type=oneshot ExecStart=/usr/local/bin/backup.sh ExecStartPost=/usr/bin/curl -fsS -m 10 --retry 3 -o /dev/null https://ping.lastping.dev/<your-monitor-id>

The failure reporter — alert immediately

OnFailure starts this unit whenever the service enters a failed state. It's optional — a failed run also sends no success ping, so the grace window would alert anyway — but /fail makes it immediate:

# /etc/systemd/system/backup-fail.service [Unit] Description=Report backup failure to LastPing [Service] Type=oneshot ExecStart=/usr/bin/curl -fsS -m 10 --retry 3 -o /dev/null https://ping.lastping.dev/<your-monitor-id>/fail

The timer

Persistent=true runs a missed activation at the next boot instead of skipping it silently:

# /etc/systemd/system/backup.timer [Unit] Description=Run nightly backup at 03:00 [Timer] OnCalendar=*-*-* 03:00:00 Persistent=true [Install] WantedBy=timers.target

Enable and test the whole loop once

sudo systemctl daemon-reload sudo systemctl enable --now backup.timer # run the service once by hand — the ping should land in the console sudo systemctl start backup.service # confirm the next scheduled activation systemctl list-timers backup.timer

Optional: catch hung runs too

An ExecStartPre ping to /start arms overrun detection — if the matching success doesn't arrive inside the grace window, LastPing opens an incident while the job is still wedged:

ExecStartPre=/usr/bin/curl -fsS -m 10 --retry 3 -o /dev/null https://ping.lastping.dev/<your-monitor-id>/start

What this catches

The units report what they can; the silence reports the rest.

  • The service failedOnFailure pings /fail and you're alerted immediately, journal intact for the post-mortem.
  • The timer stopped firing — disabled unit, broken enablement, masked service. No ping in the expected window → incident.
  • The machine is down — nothing on the host can report that; the absence of the ping does.
  • The job hung — with the /start ping armed, a run that never completes inside grace opens an incident.
  • Recovery — the next successful run closes the incident and sends a recovery notice automatically.

Still on classic cron for some hosts? The cron guide is the same pattern in one crontab line. Backup jobs deserve the fuller start → work → success/fail pattern — or browse all guides.


Questions people ask

Front-loaded answers — the most important fact first.

  • Does ExecStartPost run if the service fails?

    No — for Type=oneshot services, ExecStartPost commands run only after ExecStart has exited successfully. That's exactly the behavior you want: a failed job sends no success ping, and the missed ping (or the OnFailure unit pinging /fail) raises the alert.

  • How do I get an immediate alert when the service fails?

    Add OnFailure=backup-fail.service to the [Unit] section, where the referenced unit is a oneshot that curls the /fail endpoint. The explicit failure signal opens an incident immediately instead of waiting out the grace window.

  • What if the whole machine is off?

    The timer can't fire and nothing on the host can self-report — but LastPing alerts anyway, because the expected ping never arrives. Absence-based detection covers every failure mode, including the ones the machine can't see.

  • systemd timer or cron — does it matter for monitoring?

    No. Both patterns end in the same ping to the same URL; systemd just gives you nicer hooks (ExecStartPost, OnFailure) than a shell &&. The cron guide covers the crontab one-liner.

  • Is this free?

    Yes. LastPing is free and fully hosted with no monitor cap and no paid tier. See how it compares to Healthchecks.io and Cronitor.

FREE · FULLY HOSTED · NO PAID TIER

systemctl --failed is a list of alerts you never got.

Three unit files now beat a journal archaeology session later. First alert wired up in minutes — and LastPing monitors itself the same way.