A local dead-man's-switch for cron jobs. Cron never tells you when a job
stops running — the backup that silently hasn't fired in three days, the
sync that's been crashing since Tuesday. deadcron notices the silence and
alerts you. Zero dependencies, no server to host.
# Replace your crontab command with a wrapped version:
*/30 * * * * npx deadcron run backup --every 1h -- /usr/local/bin/backup.sh
# Then let the watcher check for silence:
npx deadcron install --every 5m"Cron jobs do not tell you when they fail. That is the core issue."
The classic incident: a database backup script dies at 2 a.m., keeps "running"
in cron's eyes, and nobody finds out until the day you actually need the backup.
Hosted monitors (healthchecks.io, Cronitor, Dead Man's Snitch) solve this — but
they need an account and send your job metadata to a third party. deadcron
runs entirely on your machine: a small state file plus a watcher you schedule
yourself.
- Each run checks in. Either wrap the command with
run(it pings on exit 0, records the exit code on failure) or callping <name>at the end of your script. - A job declares its expected period with
--every(plus optional--grace). If the time since the last successful check-in exceedsevery + grace, the job is overdue. - The watcher runs
checkon its own schedule (you add it to cron once viainstall). When a job is overdue or its last run failed, it fires every alert channel you've enabled.
# Recommended: wrap the command. Auto-registers, pings on success,
# records the exit code on failure.
deadcron run backup --every 1d --grace 1h -- /opt/backup.sh
# Or ping manually at the end of a script:
deadcron register backup --every 1d --grace 1h
deadcron ping backup # add to the tail of your job
# Inspect health any time:
deadcron status
# ● backup ok every 1d last: 3h ago
# ● sync OVERDUE by 2h every 1h last: 3h agodeadcron check # evaluate now; exit 1 if anything is overdue/failed
deadcron check --json # machine-readable
deadcron install --every 5m # add `check` to your crontab (idempotent)
deadcron uninstallcheck throttles repeat alerts (default: at most once per hour per job) so a
job that's been down for hours doesn't spam you every tick.
check fires every enabled channel at once. Terminal is on by default.
deadcron config show
deadcron config enable macos # native macOS notification
deadcron config set-webhook https://hooks.slack.com/services/XXX
deadcron config set-email --to ops@you.com --sendmail
deadcron config set-email --to ops@you.com --from cron@you.com \
--smtp-host smtp.gmail.com --smtp-port 465 \
--smtp-user me@gmail.com --smtp-pass "app-password"
deadcron config test # send a sample alert through all of them| Channel | How |
|---|---|
| terminal | writes the alert to stderr (great for CI / piping) |
| macOS | native banner via osascript |
| webhook | POST JSON {event, checkedAt, jobs} — Slack/Discord/your endpoint |
local sendmail, or direct SMTP (TLS 465, optional AUTH LOGIN) |
deadcron pause <name> # stop alerting (maintenance window)
deadcron resume <name>
deadcron fail <name> # manually mark the last run as failed
deadcron rm <name>State and config are plain JSON under ~/.deadcron/ (override with
$DEADCRON_HOME). Nothing leaves your machine unless you configure a webhook
or email channel.
| Code | Meaning |
|---|---|
0 |
all jobs healthy |
1 |
one or more jobs overdue or failed |
2 |
error (bad args, unknown job, crontab failure) |
30s · 5m · 2h · 1d · 1w (a bare number means seconds).
MIT