Every way a cron job fails
Nine distinct failure modes for scheduled jobs, what each one looks like in production, and which of them a curl at the end of your script can actually catch.
A cron-monitoring vendor comparing itself to healthchecks.io. Where deadpost is genuinely different, where healthchecks.io is genuinely better, and how to migrate in about a minute.

We build a cron monitor. You are reading a page about our closest competitor. You know how these usually go.
So let’s start with the part that vendor comparison pages normally omit:
What follows is the case for the other choice, stated in a way that would survive a healthchecks.io user reading it.
It is worth being specific, because “the incumbent is fine” is a boring claim and the details are what make it credible.
The model is correct. A check-in URL, a period, a grace window, and silence as the signal. This is the right primitive for scheduled work, and healthchecks has been the clearest expression of it for years.
It is genuinely open source. BSD-3, the whole application, not an “open core” with the useful parts behind a licence gate. If your compliance posture requires monitoring inside your own perimeter, that is not a feature we can compete with — we’re SaaS, and pretending otherwise would be silly.
Its integrations are far broader than ours. Paid plans include SMS, WhatsApp and phone-call credits, plus a long list of chat and paging targets. We ship email and Slack. That gap is real and it is not closing this quarter.
It handles concurrent runs better than we do. healthchecks.io supports run
IDs, so two overlapping invocations of the same job produce two correctly
attributed runs. In deadpost, a second /start before the first run closes
abandons the first — visible after the fact, but not correctly attributed. If
your jobs legitimately overlap, that difference matters and it favours them.
Not “different branding” — five specific behaviours. Two of these are things we built because they annoyed us elsewhere.
A ping token is a credential. It ends up in crontabs, CI secrets, Ansible vars and at least one Slack thread from 2024. Eventually someone leaks one, and the options are usually “rotate it and break every job until every config is updated” or “don’t rotate it.”
deadpost rotates with an overlap window: the new token is issued while the old one keeps accepting pings, for a window you choose (five minutes to a day). Roll out the new value at your own pace, and the old one expires on schedule. No coordinated deploy, no gap in coverage.
I’m not aware of a documented equivalent in healthchecks.io. If you’ve never needed this, it will sound like a small thing. If you have, you already know why it’s the first item on this list.
Nearly every monitoring tool has one “shut up” button, and it is overloaded to mean two incompatible things.
Conflating those means either alert fatigue or a monitor that was silently off for three weeks. Keeping them separate is not clever engineering; it’s just refusing to overload one verb.
Declare a job as hourly, run it every 70 minutes, and you have a monitor that flaps forever. deadpost watches the actual ping intervals and, when the observed cadence has clearly diverged from the declared one, offers the correction as a one-click suggestion. Accept it or dismiss it — it will not silently rewrite your config, and dismissing it makes it stop asking.

Per monitor you get a consecutive-miss threshold, quiet hours in a named timezone, a separate alert class for runs that take more than 2.5× their historical average, and an escalation ladder — “tell me again at 1h, then at 4h, then keep telling me every 4h until it recovers.”
The bit I’d defend hardest is the plain-English preview at the bottom of that panel. Alerting config is write-once, read-never, and the failure mode is believing you configured something you didn’t. A sentence describing the resulting behaviour is worth more than the controls above it.
Every run can carry tags — rows written, bytes dumped, records processed:
import deadpost
with deadpost.run() as run:
rows, size = dump_database()
run.tag(rows=rows, bytes=size)
A comparison table where every row favours the author is a marketing document, not information. Here is the other direction.
| healthchecks.io | deadpost | |
|---|---|---|
| Self-hosting | ✅ BSD-3, full app | ❌ SaaS only |
| SMS / WhatsApp / phone | ✅ on paid plans | ❌ |
| Notification integrations | dozens | email + Slack |
| Concurrent-run attribution | ✅ run IDs | ⚠️ second start abandons the first |
| Track record | since 2015 | new |
| Cost at ~100 checks | lower | higher |
| Zero-downtime token rotation | not documented | ✅ |
| Pause ≠ snooze | one concept | two |
| Schedule auto-detection | — | ✅ suggestion + accept |
| Slow-run alerts | — | ✅ 2.5× baseline |
| Escalation ladder | — | ✅ with repeat-until-recovery |
| healthchecks.io | deadpost | |
|---|---|---|
| Free | $0 — 20 checks | $0 — 10 monitors |
| Entry paid | $5 supporter · $20 for 100 checks | $19 for 50 monitors |
| Next tier | $80 for 1000 checks | $49 — unlimited monitors |
Read that table honestly and it says: healthchecks.io is cheaper than us until you get fairly large. Their free tier is twice ours. Their $20 plan carries twice the checks of our $19 one. We only come out ahead past a few hundred monitors, where “unlimited at $49” beats “$80 for 1000”.
We are not trying to win on price, and a comparison page that buried this would be worth less than nothing to you. Our pricing is exclusive of tax, which is added at checkout.
If you do want to try it, we ship an importer specifically for this, because “re-create forty checks by hand” is a real reason not to bother.
Paste a read-only healthchecks.io API key. We call their management API once, list your checks, and show you exactly what we’d create:

The details that matter:
0 4 * * * in Europe/Berlin
imports as a timezone-aware cron monitor on that expression, not as a lossy
“every 24 hours” interval.OnCalendar schedules are the common case — we speak standard 5-field cron,
and a check we can’t map appears in the skip banner with the reason.Your existing check-in URLs keep working at healthchecks.io throughout, because we never touch them. Run both in parallel for a week; that is the sane way to evaluate any monitoring change, and it costs you nothing but two curl lines.
Stay on healthchecks.io if you want to self-host, you need SMS or phone calls, your jobs run concurrently and you need per-run attribution, you’re optimising cost around 100 checks, or you simply like it. All good reasons. It is a genuinely well-built product and we’d rather you use it than use us unhappily.
Try deadpost if token rotation has ever been a coordination problem, you’ve been bitten by a paused monitor nobody resumed, you want alerting that escalates on a schedule instead of firing once into a muted channel, or you want run durations and per-run numbers as first-class data.
And if you’re not monitoring your cron jobs at all — which is most people — please go set up something today. The choice between us matters far less than the choice between “monitored” and “we’ll find out eventually.”
Nine distinct failure modes for scheduled jobs, what each one looks like in production, and which of them a curl at the end of your script can actually catch.
Grace periods, miss thresholds, escalation ladders and quiet hours — how to tune cron alerting so it wakes you for real failures and stays quiet for everything else.