Cron Job Dead Man's Switch: Heartbeat Monitoring Explained

Cronradar··8 min

What is a dead man's switch for cron jobs? A dead man's switch is a monitor that alerts you when an expected signal stops arriving, rather than when an error occurs. For cron jobs, the job sends a "heartbeat" ping each time it completes; the monitoring service expects that ping on a schedule, and if it doesn't arrive within a grace period, it raises an alert. This catches the failure mode ordinary monitoring misses — a job that silently stops running altogether, producing no error and no log to inspect.

The term dead man's switch comes from machinery: a control that must be held down to keep a machine running, so that if the operator collapses, the machine stops safely. Software monitoring borrows the idea and flips it — a signal that must keep arriving, so that when it stops, someone gets alerted. For scheduled jobs, it's the single most important kind of monitoring, because it catches the failure every other tool misses.

Why Cron Specifically Needs One

Almost all monitoring is error-driven: something goes wrong, an error is raised, you react to it. That works right up until the failure mode where nothing runs at all. A cron job that's skipped produces no error, no stack trace, no log line — there's simply an absence where an execution should have been. The classic example: a nightly database backup that has run flawlessly for eight months quietly stops, because a deploy replaced the crontab, or the server rebooted and cron didn't restart, or the disk filled and the job couldn't launch. You find out weeks later, at the exact moment you need that backup and it isn't there. Error-driven monitoring is structurally blind to this. A dead man's switch is built for it.

How Heartbeat Monitoring Works

Three moving parts:

  1. The ping. Your job makes a tiny HTTP request when it finishes successfully — a single curl or one SDK call. That request is the heartbeat.
  2. The expected schedule. You tell the monitor when to expect the ping — for example "every day at 02:00" as a cron expression. Now the monitor knows what "on time" means.
  3. The grace period. A tolerance window before a late job is flagged, so normal variance (a backup that sometimes takes ten minutes, sometimes twenty) doesn't page you. Configure it in seconds — gracePeriod=900 is a 15-minute window.

If the ping lands inside the window, the monitor is healthy. Late but still in grace: warning. Past grace with no ping: critical, and you're alerted. Because the alert is driven by the absence of the expected signal, it fires even when the job's machine is completely dead — the monitoring lives outside your infrastructure, so it doesn't depend on the thing it's watching.

Success Ping vs. Full Lifecycle

The minimal pattern is a single completion ping — enough to answer "did it run on schedule?" A richer pattern sends a start ping and then a complete (or fail) ping. That does two extra things: it measures how long each run takes, and it catches a job that started but hung — one that would look fine to a completion-only check because the start happened. Begin with the completion ping; add start/complete once you care about duration or hangs.

Adding It — One Line

The mechanism is deliberately trivial. Append a ping to the end of your cron line. The URL is /ping/{monitor-key}/{api-key} — monitor key first, API key last:

# Ping on success (&& ensures the ping only fires if the job succeeded)
30 2 * * * /opt/backup.sh && curl -fsS "https://cron.life/ping/backup/ck_app_12345_abcdef"

The first time you ping, include ?schedule= and the monitor creates itself — no dashboard setup, and it self-heals if the monitor is ever deleted:

# First run auto-registers the monitor; gracePeriod is in seconds
curl -fsS "https://cron.life/ping/backup/ck_app_12345_abcdef?schedule=0%202%20*%20*%20*&gracePeriod=900"

To record failures explicitly — which trigger an immediate alert, without waiting for the grace window — ping the complete and fail endpoints:

30 2 * * * /opt/backup.sh \
  && curl -fsS "https://cron.life/ping/backup/ck_app_12345_abcdef/complete" \
  || curl -fsS "https://cron.life/ping/backup/ck_app_12345_abcdef/fail?message=backup%20failed"

Auto-Discovery for Frameworks

Hand-rolled curl lines work, but they're one more thing to remember on every new job. If your jobs live in a framework scheduler — Laravel's schedule(), Hangfire recurring jobs, Celery beat, Quartz, node-cron — the cronradar.com">CronRadar SDK discovers those recurring jobs automatically and monitors each one, with no per-job wiring. Every job is monitored by default; opt a specific one out with [SkipMonitor] (.NET), @skip_monitor (Python), or ->skipMonitor() (PHP/Laravel). This auto-discovery is the part most heartbeat services — and hand-written curl — don't do.

Frequently Asked Questions

Dead man's switch vs. uptime monitoring — what's the difference?

Uptime monitoring checks that a URL or server is reachable. A dead man's switch checks that a scheduled job actually ran. They point in opposite directions: uptime pings in, a dead man's switch waits for a ping out.

What if a job legitimately takes longer than usual?

The grace period absorbs normal variance — set it wide enough to cover your job's worst realistic run time, and you won't get false alarms.

Do I have to register every job manually?

No. With a framework scheduler, jobs are auto-discovered; with a standalone script, the first ping that includes ?schedule= self-creates the monitor.

Does it still alert if my whole server goes down?

Yes — that's the point. The monitor runs externally, so an absent ping from a dead server is exactly what trips the alert.

Related Articles

Start monitoring your cron jobs

Get started in minutes. No credit card required.

Cron Job Dead Man's Switch: Heartbeat Monitoring Explained | Cronradar