Back to blog

Reference · September 4, 2026

Dead man's switch, explained for developers (and how to actually build one)

Topics:Monitoring
A dead man's switch is the developer pattern for “alert me when the silence is the problem.” This post explains the pattern, then shows how to ship it with native Crontap Heartbeats.
crontap.com / blog
Learn how dead-man monitoring works, then add a native Crontap heartbeat with period, grace, /fail, alerts, and 90-day history.

A dead man's switch is the developer pattern for "alert me when the silence is the problem." Most of your monitoring stack watches for things going wrong: a 500, a slow request, a queue backing up. A dead man's switch watches for nothing happening when something was supposed to. Your nightly backup script that never logged today. The worker that crashed at 3am. The cron that the platform silently throttled. This post is the developer-flavored explainer for what a dead man's switch is, when you actually need one, and how to cover it with Crontap (plus when to add a dedicated absence monitor).

For the heartbeat vs uptime distinction (they catch different failures), start with Cron heartbeat vs uptime monitor. For the full Healthchecks.io pairing walkthrough, see Pair Crontap with Healthchecks.io for end-to-end monitoring.

What a dead man's switch is

The literal mechanical version

The name comes from railways. A dead man's switch on a train requires the driver to hold a lever or pedal. If the driver becomes incapacitated and releases it, the brakes engage automatically. The safety mechanism is tied to continuous human (or machine) presence, not to detecting a specific failure mode.

The software version

In software, the same idea shows up as a heartbeat or dead-man check: something that should happen on a schedule pings a monitor URL. If the ping does not arrive inside a tolerance window, the monitor pages you. The alert is about absence, not about an error response.

"Fail deadly" and why it is the same idea

"Fail deadly" is the security framing: the system defaults to the dangerous outcome unless something actively proves it is still healthy. A dead man's switch inverts that for ops: unless your job actively checks in, you assume it failed. Hosted heartbeat services phrase it as monitoring "did this thing happen?" rather than "did this HTTP call return 200?"

When to use one (five developer scenarios)

  1. Nightly backup or ETL. The job runs at 2am. If it does not run, nobody notices until someone asks for yesterday's data. A dead-man check pages you when the success ping never arrives.

  2. Cron job that pushes data into a warehouse. Stripe sync, HubSpot export, Shopify inventory pull. The dashboard looks fine because it still shows last week's numbers. Silence is the bug.

  3. Long-running worker process. A queue consumer or sidecar that should heartbeat every N minutes. Process death is invisible to an uptime URL check on the public site.

  4. CI or scheduled pipeline heartbeat. GitHub Actions, Render cron, or an external scheduler fires a workflow. You want an alert when the workflow never started, not only when it failed mid-run.

  5. Personal or small-team "sanity" jobs. A script that emails you a digest, rotates logs, or renews a token. Low traffic, high consequence if it stops.

For scheduled HTTP work on Crontap, see monitoring heartbeats and uptime monitoring.

How a dead man's switch works in practice

The two halves: the thing that should fire vs the watcher

Every implementation has the same shape:

[Scheduler]  →  runs job  →  job succeeds  →  ping monitor URL

                                └── job fails or never runs → no ping → alert

The scheduler owns the clock (system cron, Crontap, GitHub Actions, platform cron). The job does the work and, on success, hits the monitor. The watcher (a hosted heartbeat service, or a second schedule that pings a dead-man URL) holds the tolerance window and fires when the ping is late or missing.

Why uptime monitoring is not the same thing

An uptime monitor asks: "Is this URL up right now?" It does not know your backup was supposed to run at 2am. A dead-man check asks: "Did I hear from this job inside the window I expected?"

Crontap uptime is built for the first question: paste a public URL, pick a probe interval, and notify connected channels when probes fail. That is the right tool when customers hit your API and you need a green/red chart. It is not a substitute for absence detection on a background job.

Crontap schedule failure alerts cover another slice: the schedule fired, and the HTTP call returned 4xx, 5xx, or timed out. Crontap Heartbeats cover the silent slice: a recurring job did not send its expected ping. We walk through all three layers in Cron heartbeat vs uptime monitor.

Period versus grace

Every heartbeat needs two numbers:

  • Period is how often a successful ping should arrive. A job that runs every hour gets a one-hour period.
  • Grace is how late the job may be before the miss becomes an incident. It absorbs normal runtime variance, queue delay, and a slightly busy host.

A heartbeat with a one-hour period and ten-minute grace is not down at 60 minutes. It becomes down after 70 minutes without a success ping. Crontap detects that missed deadline within one minute after grace. Connected Notifications and generic machine webhook Integrations then run asynchronously, so the one-minute promise applies to detection rather than delivery.

For hourly jobs, start with 5 to 10 minutes of grace. For daily jobs, 30 to 60 minutes is usually calmer. Weekly jobs often deserve a few hours. Use real runtime variation, not optimism, as the input.

How to set one up with Crontap Heartbeats

1. Create the heartbeat

Open Crontap Heartbeats, create a heartbeat, and set the expected period plus grace. Starter supports hourly or slower heartbeats. Paid plans allow periods down to one minute.

Crontap gives the heartbeat a secret ping URL:

https://ping.crontap.com/YOUR-TOKEN

Treat it like a password. Anyone holding the URL can report success or failure for that heartbeat, so keep it out of public repositories and shared logs.

2. Ping after the work succeeds

For a simple command, use && so a failed job never reports success:

/usr/local/bin/generate-and-send-report.sh \
  && curl -fsS -m 10 --retry 3 "https://ping.crontap.com/YOUR-TOKEN"

For a wrapper that preserves the original exit code and reports known failures quickly:

#!/usr/bin/env bash
 
/path/to/job.sh
status=$?
ping_url="https://ping.crontap.com/YOUR-TOKEN"
 
[ "$status" -eq 0 ] || ping_url="$ping_url/fail"
curl -fsS -m 10 --retry 3 "$ping_url" > /dev/null
 
exit "$status"

Capture $? before running curl. Otherwise a successful ping can replace the job's failed exit code, making the local scheduler and the heartbeat disagree about what happened.

3. Use /fail for fast failure

Silence catches the job that never starts or never reaches its reporting step. A known error should not wait for period plus grace. Send the same URL with /fail appended:

curl -fsS -X POST \
  --data "nightly export could not reach the warehouse" \
  "https://ping.crontap.com/YOUR-TOKEN/fail"

Crontap records the failed event and queues connected email, Slack, Discord, or Telegram Notifications. Generic JSON webhooks remain separate machine Integrations.

4. Read the timeline, not only the alert

The heartbeat detail page keeps 90 days of raw/event history. That gives you evidence for late pings, explicit failures, missed deadlines, and recoveries without asking the job host for old logs.

Crontap Heartbeats v1 deliberately stays small. It does not model /start, calculate run duration, or accept cron-expression expectations. If you need "weekdays at 09:00 Europe/Berlin" or start-to-finish duration today, choose a specialist that supports those semantics.

Fix this in 60 seconds with Crontap. Free forever tier. No credit card. Create your first heartbeat →

When to keep an external watcher too

Native heartbeats remove the need for a second tool in the common setup. They do not erase the independent-watcher argument.

If Crontap schedules the job and also receives its heartbeat, one vendor owns both the clock and the alarm. Many small teams accept that trade-off for one dashboard and one bill. A regulated or high-consequence workflow may require the alerter to fail independently from the scheduler. In that case, keep Healthchecks.io, Cronitor, or another external receiver alongside the Crontap schedule.

That is a risk-model choice, not a missing-feature workaround. The Healthchecks.io pairing guide now compares Crontap alone, Healthchecks alone, and both together.

Common gotchas

Pinging too early. Put the success ping after the durable side effect. "Started generating the backup" is not the same as "the backup exists and passed verification."

Pinging on every exit path. Send the base URL on success and /fail on a known failure. Do not send a success ping from a finally block that also runs after exceptions.

Too-tight grace windows. A five-minute grace on a daily job causes false positives when the job legitimately finishes at 02:07. Give the normal tail room.

A failed ping request. Add a short timeout and a few client-side retries to the curl call. A healthy job should not look absent because the one reporting request hit a transient network error.

Secrets in ping URLs. Rotate a leaked URL in Crontap, then update the job. Do not paste live tokens into tickets or public examples.

FAQ

Is a dead man's switch the same as a watchdog timer?

Close. A watchdog timer in embedded systems resets a counter when fed periodically; if the counter expires, the system resets or halts. A dead-man check is the hosted-ops version: an external service expects a ping on cadence and alerts when the ping stops.

Can I build one without a service?

Yes, with trade-offs. You can run a second cron that checks a last_success.txt timestamp and emails when it is stale. You then maintain the checker, the alert routing, and edge cases such as paused jobs and duplicate alerts. Hosted heartbeats exist because that checker becomes another thing to monitor.

What is the minimum interval?

Starter heartbeats are hourly or slower. Paid Crontap plans support a one-minute period floor. Match the period to the job's real cadence, then add grace for normal variation.

Does Crontap uptime replace a dead-man check?

No. Uptime probes a URL from the outside. A heartbeat waits for your job to phone home. Use uptime for public URL health and a heartbeat when the fear is "the cron never fired."

Does Crontap replace Healthchecks.io?

For interval-based success, /fail, connected Notifications, generic machine webhook Integrations, and 90-day history, Crontap can replace a second heartbeat vendor. Healthchecks.io remains the stronger fit when you need self-hosting, /start plus duration, log signals, or cron-expression expectations. Pair both when operational independence matters.

Where does this fit with cron job monitoring?

Dead-man checks are one layer of cron job monitoring: schedule health, run logs, failure alerts, uptime, and heartbeat absence. Crontap ships all five, while dedicated heartbeat tools go deeper on job lifecycle telemetry.

Related on Crontap

Fix this in 60 seconds with Crontap. Free forever tier. No credit card. Create your first heartbeat →

From the blog

Read the blog

Guides, patterns and product updates.

Tutorials on scheduling API calls, webhooks and automations, plus deep dives into cron syntax, timezones and reliability.

Product Updates

Introducing AI Integrations

Transform a schedule's HTTP response with a plain-English prompt, return text or JSON, and forward it to Slack, Make, n8n, or your own endpoint. Test on any tier; saving is a Pro feature.

Alternatives

Vercel cron jobs: the Hobby once-per-day limit and how to beat it

Vercel Cron caps Hobby at one run per day, only guarantees timing within the hour, is UTC only, and ties every schedule change to a redeploy. Here is the external cron pattern teams use to ship per-minute, timezone-aware schedules and one dashboard across projects without paying $20/mo per user for Pro.