All posts
Monitoring6 min readWatchFor Team

Heartbeat & Cron Job Monitoring: the Dead Man's Switch Your Backups Deserve

Most monitoring checks that something is up. Heartbeat monitoring does the opposite — it alerts when something that should run goes silent. The concept, who needs it, and how to set it up so a dead backup job can't hide for three weeks.

Heartbeat & Cron Job Monitoring: the Dead Man's Switch Your Backups Deserve

Here's a failure mode regular uptime monitoring can't catch: your nightly backup job silently stops running. No error page, no 500, no alert — it just quietly doesn't happen. You find out weeks later, at the worst possible moment: when you actually need the backup.

Catching the absence of an event needs a different approach. That approach is heartbeat monitoring — and if you run anything on a schedule, you probably need it more than you think.

The problem: the invisible half of your infrastructure

Uptime monitoring works by reaching out — a prober requests your URL and checks the response. That covers everything with an endpoint: websites, APIs, mail servers.

But a big share of what keeps a business running has no endpoint at all:

  • Nightly backups (database dumps, snapshots, offsite syncs)
  • Cron jobs — cleanup, billing runs, report generation, certificate renewals
  • Queue workers and consumers (Sidekiq, Celery, BullMQ)
  • Data pipelines and ETL jobs
  • Scheduled syncs with third parties
  • IoT devices and edge boxes behind NAT

When one of these dies, there's nothing to ping. The failure is an absence — and absence is invisible to "is it up?" checks. Worse, these failures are silent by nature: a crashed worker throws no error page, a lost crontab entry writes no log. The only observable signal is that something which should have happened… didn't.

The core insight: some failures are the absence of success. A dead backup job doesn't send an error — it sends nothing. Only by expecting a regular signal can you detect "this should have happened and didn't."

What heartbeat monitoring is

Heartbeat monitoring (also called a dead man's switch, check-in monitoring, or passive monitoring) flips the direction. Instead of the monitor checking the job, the job checks in with the monitor:

Job runs successfully → it pings a unique URL → all good
Job didn't run / crashed / hung → no ping arrives → alert!
Normal monitoringHeartbeat monitoring
You ping the serviceThe job pings you
Alerts when it responds wrongAlerts when it goes silent
For endpoints (sites, APIs)For scheduled work (cron, backups, workers)

The name comes from safety systems that trigger when an operator stops holding a lever. Same idea here: the alarm fires on silence, not on an error — which is exactly what catches the worst, quietest failure class.

Who actually needs this

If any of these sentences describes you, you have a heartbeat-shaped blind spot:

  • "Our backups run nightly." The classic. Backup scripts fail for mundane reasons — a full disk, an expired credential, a renamed bucket — and nobody reads backup logs until restore day. A heartbeat on the backup script turns "three weeks of missing backups" into "one morning alert."
  • "We have a worker that processes the queue." A crashed consumer lets the queue grow silently for hours while the website looks perfectly healthy. A per-cycle heartbeat catches the crash in minutes.
  • "Billing runs on the 1st of every month." Low-frequency jobs are the most dangerous: a monthly job that failed has already cost you a month by the time anyone wonders about it.
  • "The device is behind the customer's firewall." You can't probe into NAT — but the device can always ping out. One heartbeat per unit tells you which box went dark and when.
  • "That server migration was months ago." Migrations are where crontab entries go to die. A heartbeat per job is the only way to notice the ones that didn't survive the move.

The anatomy of a good heartbeat monitor

The concept is simple, but the details decide whether it's trustworthy or noisy. Four things matter:

1. Schedules that match reality

Two modes cover everything: a fixed interval ("I should hear from this worker every 5 minutes") and a real cron expression ("03:00 daily, Europe/Vilnius") — timezone-aware, DST included. If your monitor can't speak cron, you end up approximating "every weekday at 3 AM" with a 24-hour interval and eating false alarms on the weekend.

2. A grace period

Jobs are never perfectly punctual — a backup that takes 10–40 minutes depending on data size is healthy, not broken. Grace is how much lateness you forgive before alerting: tight for frequent workers (a minute), generous for long-running jobs. Without it, you get paged for a job that's simply five minutes slow; with too much of it, you learn about failures late. Tune per job.

3. "Didn't run" and "ran but failed" are different problems

A ping's presence says the job ran. But what if it ran and failed? A good heartbeat setup reports the outcome too — the process exit code appended to the ping (/0 for success, /2 for a failure, /fail for an explicit failure signal). That splits your alerts into two distinct stories:

  • Missed schedule → the job never started (dead cron, dead machine, dead container)
  • Run failed → the schedule is alive, but the work itself is broken

Different problems, different fixes, different urgency. Collapsing them into one alert loses exactly the information you need at 3 AM.

4. Run duration and context

Send a ping at the job's start and another at the finish, sharing a run ID, and every run's duration gets measured — so "the export suddenly takes 40 minutes instead of 5" becomes an alert before it becomes an outage. And when the job attaches its last log lines to the ping, diagnosing a failed run starts in your monitoring dashboard, not with SSH.

Setting it up is one line

The integration cost is deliberately near zero. For a cron job:

# crontab: define the ping URL once, report success or failure
PING=https://ingest.watchfor.io/p/<your-token>

0 3 * * * backup.sh && curl -fsS $PING/0 || curl -fsS $PING/fail

Any HTTP client works — curl from a shell script, a fetch() in a Node worker's loop, an HTTP call from a Kubernetes CronJob. The URL token is the only secret; there's no agent, no SDK, no firewall hole.

Common pitfalls

  • Monitoring the machine instead of the job. A server that's up says nothing about whether cron ran your script. The heartbeat has to come from the job itself, at the end of a successful cycle.
  • One heartbeat for many jobs. If five jobs share a ping URL, four can die unnoticed while the fifth keeps checking in. One monitor per job — they're cheap.
  • Pinging before the work is done. A ping at the start of the script tells you the script started, not that the backup succeeded. Ping on completion (or use start/finish pairs and get duration tracking for free).
  • Grace set by guesswork. Watch a week of real run times, then set grace slightly above the natural variance. Revisit when the job's workload grows.

Heartbeats in WatchFor

WatchFor's heartbeat monitors implement all of the above: interval and real cron-expression schedules with timezones and grace, exit-code awareness (missed / failed / specific code alert separately), start–finish run duration tracking, and up to 10 KB of job output attached to every ping. A missed window opens an incident immediately — a daily backup that missed once has already lost a day — and the next successful check-in auto-resolves it.

A heartbeat counts as a regular monitor on every plan, including Free, and the ping URL conventions are Healthchecks-compatible — migrating existing scripts is usually just swapping the domain. Details in the docs.

The bottom line

In one line
WhatAlerting when an expected check-in doesn't arrive.
ForCron jobs, backups, workers, pipelines, IoT — anything scheduled.
vs uptimeUptime pings the service; heartbeat waits for the job to ping.
CatchesSilent failures — the job that just stops running.
Cost of skipping itYou find out when you need the backup.

Uptime checks cover what's public. Heartbeats cover what's invisible. Most teams need both — and the invisible half is usually the one nobody is watching.

Related: Uptime Monitoring 101, synthetic vs real user monitoring.

Share this article