On this page
Ask a room of engineers "is your backup running?" and watch the pause. Most backups, cron jobs, and queue workers run in the dark: no endpoint to check, no dashboard, no one watching. They work for months, then quietly stop — a changed path, a rotated credential, a full disk — and nobody finds out until the day the backup is needed and isn't there.
The short version
- Scheduled jobs fail silently because there's nothing external to poll — normal uptime checks can't see them.
- A heartbeat (dead man's switch) alerts you when an expected check-in doesn't arrive on time.
- The job pings a unique URL on success; you alert when now > last ping + interval + grace.
- Add optional start and fail pings for run duration and explicit failure detail.
- It's the only reliable way to catch a job that never ran at all.
The silent-failure problem
Regular uptime monitoring works by reaching out: it requests your URL, connects to your port, resolves your DNS. That model breaks down completely for scheduled work, because a cron job has no door to knock on. It wakes up, does its thing, and exits. There's no service to poll, so there's nothing for a normal monitor to watch — and the most dangerous failure of all, the job that never ran, produces no error, no log line, no signal whatsoever.
What a heartbeat is
A heartbeat — also called a dead man's switch — inverts the direction of monitoring. Instead of you checking the job, the job checks in with you. Each run sends a small signal ("still alive, just finished"). The monitoring system knows how often that signal should arrive. When one is late, it alerts.
The elegance is in what it catches. A crashed job doesn't ping — caught. A job that was never scheduled doesn't ping — caught. A server that's powered off doesn't ping — caught. Silence itself is the alert.
How it works
The mechanics are simple:
- You create a heartbeat and get a unique ping URL — the URL is the credential, so treat it like a secret.
- You tell it the expected interval (e.g. "every hour") and a grace period.
- Your job requests that URL as its final step, but only on success.
- If no ping arrives within
interval + grace, the heartbeat is marked overdue and you're alerted.
It never false-alarms on day one
Choosing interval and grace period
Two numbers define a heartbeat, and getting them right is the whole game between false alarms and slow detection:
- Interval — how often the job is supposed to check in. Match it to the schedule: hourly cron → 1 hour.
- Grace period — how much lateness to tolerate before alarming. This absorbs normal run-time variance so a slightly slow run doesn't page you.
The rule: grace should cover the job's worst reasonable run time plus a small buffer.
| Job | Interval | Grace | Reasoning |
|---|---|---|---|
| Hourly sync (runs ~2 min) | 1 hour | 10 min | Absorbs a slow run without hiding a real miss |
| Nightly backup (runs ~40 min) | 24 hours | 2 hours | Backups vary with data volume |
| Every-5-min worker | 5 min | 3 min | Tight loop; catch stalls fast |
Start and fail signals
A plain success ping is enough to catch silent death, but two optional signals make heartbeats far more useful:
The start ping
Ping a /start variant when the job begins and the success URL when it ends. The gap between them is your job's run duration — now you can catch a job that's still running but has slowed to a crawl, not just one that died.
The fail ping
If your job can detect its own failure, have it ping a /fail URL in that case. That turns "we heard nothing, maybe it's late" into "the job ran and explicitly reported it failed" — a stronger, faster signal you can route at higher severity.
What to monitor this way
- Backups — database dumps, file snapshots, offsite syncs. The classic.
- Cron jobs — cleanup tasks, report generation, cache warmers, renewals.
- Queue & background workers — anything that should tick continuously.
- Scheduled ETL / data pipelines — where a missed run corrupts downstream data.
- Certificate renewal jobs — pair with expiry monitoring for belt and braces.
Setting one up
The whole integration is usually one line appended to a job. Add the ping as the last command so it only fires on success:
# Runs the backup, then pings on success (&& short-circuits on failure)
0 3 * * * /usr/local/bin/backup.sh && curl -fsS -m 10 https://ensju.com/heartbeat/enshb_xxx > /dev/nullFor a script where you want start, success, and fail signals:
URL="https://ensju.com/heartbeat/enshb_xxx"
curl -fsS -m 10 "$URL/start" > /dev/null # mark the run as begun
if /usr/local/bin/do-backup; then
curl -fsS -m 10 "$URL" > /dev/null # success
else
curl -fsS -m 10 "$URL/fail" > /dev/null # explicit failure
exit 1
fiTip
curl -fsS -m 10: -f fails on HTTP errors, -ssilences the progress bar, -S still shows real errors, and -m 10caps the ping so a hung request never wedges your job.Common mistakes
- Pinging unconditionally. If you ping regardless of outcome (e.g. before the real work, or with
;instead of&&), a failing job still looks healthy. Ping after success. - Grace too tight. A grace shorter than the job's natural variance turns every slow run into a page. Start generous, tighten later.
- Grace too loose. A 24-hour grace on an hourly job means a full day of silent failure before you hear about it. Match grace to the cost of a missed run.
- Leaking the ping URL. The URL is the credential. Keep it out of public repos and rotate it if it's exposed.
Heartbeats are the missing half of monitoring — the part that watches what you can'treach. In Ensju they're a first-class resource (included on the free plan), with start/fail pings, per-job intervals and grace, and alerts through the same channels as everything else. For the outward-facing half, see what uptime monitoring is.
Frequently asked questions
- What is a dead man's switch in monitoring?
- It's a monitor that alerts you when an expected signal stops arriving. Instead of checking whether a service is up, it waits for a job to check in — and pages you if the check-in doesn't happen on time. It's the standard way to monitor cron jobs, backups, and background workers.
- How do I monitor a cron job?
- Give the job a unique ping URL and have it make a request to that URL as its last step when it succeeds. The monitoring system knows the expected interval; if a ping doesn't arrive within that interval plus a grace period, it alerts. Optionally ping a start URL first and a fail URL on error for richer detail.
- Why can't I just monitor cron jobs with normal uptime checks?
- Uptime checks reach into a service from outside. A cron job has no endpoint to reach — it runs, finishes, and exits. There's nothing to poll. Heartbeats invert the direction: the job reports to you, so a job that never runs (or dies silently) is exactly what gets caught.
- What grace period should I use for a heartbeat?
- Set the grace period to cover the job's normal run-time variation plus a small buffer — enough that a slightly slow run doesn't false-alarm, but short enough that a truly failed run is caught quickly. For a job that usually finishes in 2 minutes on an hourly schedule, a 5–10 minute grace is reasonable.