Monitor Heroku Scheduler jobs — detect failed and skipped tasks
The Heroku Scheduler add-on runs your task in a one-off dyno every 10 minutes, hourly, or daily. It's explicitly best-effort: a run can be delayed or skipped, and a task that exits non-zero produces no notification — the failure lives only in your logs. Append one curl ping to the job command and LastPing turns "hope it ran" into a signal you can trust.
Setup: append a ping to the job command
One config var and one edit in the Scheduler dashboard. Works for any buildpack.
Create a heartbeat monitor in LastPing
In the console, create a monitor matching your Scheduler frequency (every 10 min / hourly / daily). Heroku Scheduler runs in UTC — set the monitor to UTC. Give daily jobs a generous grace window since Scheduler is best-effort. Copy the ping URL.
Store the ping URL as a config var
Keep the URL out of the dashboard command by setting it as a config var:
Append the ping to the Scheduler job command
In Resources → Heroku Scheduler → the job, add && curl … so the ping only fires on a clean exit. Add || curl …/fail to be paged immediately on failure:
Prefer a rake/management task? Wrap it in a script
If the command is complex, put it in bin/scheduled-digest in your repo, ping at the end of the script, and set the Scheduler command to bin/scheduled-digest. Same effect, cleaner dashboard.
What this catches
- Task exits non-zero — success ping skipped (or /fail fires); silence opens an incident.
- Scheduler skips a run — best-effort means runs can be dropped; no ping → incident opens.
- Dyno never boots — a failed release or bad config keeps the one-off dyno from starting; no ping → incident.
- Recovery — the next successful run closes the incident automatically.
On Render? See Render cron jobs. On Railway? See Railway cron jobs. Running Rails/Sidekiq? See Sidekiq. For the general pattern, see Monitor cron jobs.
Questions people ask
Front-loaded answers — the most important fact first.
-
Does Heroku Scheduler alert when a job fails?
No. Scheduler runs your task in a one-off dyno and does not notify you when it exits non-zero — the failure appears only in
heroku logs. Add-ons like a log drain can surface it, but nothing tells you a run was skipped. A dead-man's-switch ping catches failures and skips by expecting a check-in each run. -
Is Heroku Scheduler guaranteed to run my job on time?
No. Heroku documents Scheduler as best-effort: jobs usually run near the scheduled time, but can be delayed or skipped and it is not guaranteed. For anything important (billing, backups, digests) that unreliability is exactly why an external watchdog matters.
-
What timezone does Heroku Scheduler use?
UTC. All Scheduler times are interpreted in UTC regardless of your app's
TZconfig var, so set your LastPing monitor to UTC too. -
Is this free?
Yes. LastPing is free and fully hosted with no monitor cap and no paid tier. See vs Healthchecks.io and vs Cronitor.
Best-effort scheduling deserves a real watchdog.
Append one curl to the job command. First alert in under a minute — and LastPing monitors itself the same way.