Heroku tells you a lot about your app. Dyno load, Postgres connection counts, response times, error rates: it's all in the dashboard, and Threshold Alerting will email you when error rate or response time crosses a line you set. What it won't tell you: whether the app is actually reachable from outside Heroku's network, or whether a Scheduler job you haven't thought about in months actually ran today. Those gaps are structural, not a config you forgot to flip.
What does Heroku already monitor for you?
Out of the box, a Heroku app gives you:
- Dyno and Postgres metrics: memory, load, connection counts, query stats, visible in the dashboard once you enable
log-runtime-metrics. - Logs, tagged by process type (
web.1,scheduler.1, and so on), pulled withheroku logsor shipped to a log add-on. - Threshold Alerting: a native feature that watches error rate and response time and emails you (or an alert tool's email integration) when they cross a threshold you configure. Heroku recommends keeping response times under 500ms.
- Third-party APM and error tracking: New Relic, Scout, Sentry, Bugsnag, and similar add-ons plug into the platform for deeper traces and exception capture.
That's a reasonable baseline, and for a lot of small apps it's genuinely enough day to day.
What's missing from Heroku's own monitoring?
Everything above measures requests your app already received, from inside Heroku's own infrastructure. That leaves two gaps Heroku's own documentation doesn't address at all:
No outside-in check. If routing breaks, DNS misresolves, a cert expires, or the dyno is simply down, no request reaches your app to be measured. Threshold Alerting has nothing to alert on, because from its vantage point nothing happened. You need something polling your public URL from outside Heroku's network to catch that class of failure.
No Scheduler monitoring, at all. Heroku's own guide to monitoring apps doesn't mention Scheduler or cron jobs once. There's no built-in way to know whether a scheduled job ran, skipped, or hung.
There's also a smaller gap worth knowing about: Threshold Alerting is email-only. If you want alerts in Slack, Telegram, or Discord instead of an inbox, that has to come from a third-party monitor, not from Heroku itself.
This matters more than it sounds for an app with no human-facing traffic to notice trouble first, like an API backing a mobile app. Nobody's refreshing a page and complaining when a background API degrades; something external has to be watching.
Is Heroku Scheduler actually reliable?
Mostly, but Heroku is upfront that it isn't guaranteed. Its own Scheduler docs state that job execution is "expected but not guaranteed" and that Scheduler is "known to occasionally (but rarely) miss the execution of scheduled jobs," with rarer cases where "a job may be skipped" or "a job may run twice."
A few other mechanics worth knowing:
- Jobs run as one-off dynos, logged under a process name like
scheduler.1(heroku logs --ps scheduler.1). - The minimum interval is 10 minutes. There's no sub-10-minute scheduling.
- A dyno started by Scheduler is terminated after roughly its own scheduling interval. A job set to run every 10 minutes gets killed around the 10-minute mark even if it's still working.
None of that shows up as an error anywhere. The job just doesn't run, or gets cut off, and the dashboard stays quiet.
How do I add an external uptime check to a Heroku app?
Point a monitor that lives outside Heroku at your app's public URL, on a short interval, and have it alert somewhere other than email. The setup itself is platform-agnostic:
- Pick a URL the monitor can poll every 30–60 seconds, ideally a lightweight route rather than a page that does real work.
- Configure the monitor's alert channel. Telegram, Slack, or Discord gets noticed faster than an inbox that also has every other automated email in it.
- Set it up once per dyno-backed app, including internal APIs with no browser-facing traffic. Those are exactly the ones nobody else is watching.
This is the same "watch it from outside" principle covered in more depth in how to monitor a cron job, just applied to the app's main endpoint instead of a background task.
How do I set up a heartbeat for a Heroku Scheduler job?
Add one line to the end of the job's command, after the real work, so it only fires on success:
your-job-command && curl -fsS -m 10 --retry 3 "https://ingest.canaryhub.ai/ping/YOUR_TOKEN" > /dev/null
Set the monitor's expected interval to match the job's Scheduler frequency, plus a grace period a bit above the job's normal runtime. Guarding the ping with && means a skipped run or a mid-job kill leaves the ping unsent, the same as a thrown exception would. Silence past the grace period is the alert.
Heroku's dashboard, Threshold Alerting, and Scheduler cover the inside view well. What they structurally can't do is check from outside or notice a quiet job. A small external monitor with a heartbeat ping, delivering to Telegram or Slack instead of an inbox, closes exactly that gap without replacing anything Heroku already does well.
