The worst outages are the quiet ones. A web server that returns 500 trips every alert you own. A cron job that simply never runs returns nothing at all — no error, no log line, no page. You find out days later when the backup you needed isn't there.
Why silent cron failures are invisible to normal monitoring
Most monitoring watches for a bad response: a 5xx status, a timeout, a failed health check. That model assumes the thing you're watching is running and talking to the network. A scheduled job breaks that assumption. When cron doesn't fire — because the machine was asleep, the crontab was edited, the container didn't restart, the disk filled, or a dependency upstream changed — there is no request to inspect and no response to grade. The absence of a run is exactly what you need to detect, and absence is the one thing a request-based check can never see.
The fix: invert the check with a dead-man's switch
Instead of you polling the job, the job checks in with you. You register an expected schedule — "I expect a ping at least once every 24 hours, with an hour of grace." Each time the job finishes successfully it sends one HTTP request to a private URL. If that ping arrives on time, all is well and you hear nothing. If the window passes with no ping, the silence itself becomes the alert. This pattern is called a dead-man's switch (or heartbeat monitoring), and it is the only reliable way to detect a job that failed by not happening.
# nightly backup — ping only after it actually succeeds 0 3 * * * /usr/local/bin/backup.sh && curl -fsS https://notdown.notdown-app.workers.dev/ping/YOUR-KEY
The && matters: it ties the ping to success. If backup.sh exits nonzero, the ping never fires, the window lapses, and you get alerted — which is what you want.
Common failure modes it catches
- Cron never fired. Laptop was closed, VM was down at 3am, the crontab lost its entry after a redeploy. No run, no ping, alert.
- The job hung. It started, blocked on a network call forever, and never reached the ping. The window lapses.
- A dependency died. An expired token or a deleted bucket makes the job exit early. Tie the ping to the exit code (below) and you're covered.
Report success vs. failure, not just "it ran"
A bare ping tells the switch the job reached the end. But a job that catches its own exception, logs it, and exits 0 will happily ping while doing nothing useful. Send the real exit code so a failure trips an alert immediately instead of waiting for the window to lapse:
backup.sh; curl -fsS https://notdown.notdown-app.workers.dev/ping/YOUR-KEY/$?
Exit 0 records success; any nonzero code is treated as a failure and alerts right away.
How much grace to allow
Set the grace window a little longer than your job's worst realistic runtime, not its average. A backup that usually takes 12 minutes but occasionally takes 40 under load should have a window comfortably past 40, or you'll get false alarms on slow nights. Too tight and you cry wolf; too loose and you learn about a dead job hours late. Start generous and tighten once you know the real distribution.
NotDown gives every job a heartbeat like this on the free tier — 1-minute resolution, alerts to Discord, Slack, or any webhook, with automatic recovery notices when the ping comes back. See how heartbeat monitoring works →