Most cron jobs run without any monitoring. They work fine for months, so there's no urgency. Then one day a job stops running — a dependency breaks, a timeout gets hit, the scheduler itself crashes — and you don't find out until someone notices that the report wasn't generated, the cleanup didn't run, or the data is stale.
This guide covers the four practical approaches to cron job monitoring, with actual setup steps for each. By the end you'll have at least one of them running.
This is the most reliable approach for detecting missed schedules. The idea: your job makes an HTTP request to a "heartbeat" URL at the end of every successful run. A monitoring service watches for that ping on the expected schedule. If the ping doesn't arrive within the window, you get alerted.
It catches two failure modes that exit-code monitoring misses:
Create a free account at healthchecks.io, create a check with your expected schedule, and copy the ping URL. Then add one line to your cron job:
# crontab entry
0 9 * * 1-5 /path/to/my-job.sh && curl -fsS --retry 3 https://hc-ping.com/your-uuid-here
The && means the ping only fires if the script exits 0. If the script fails, no ping. If healthchecks.io doesn't see a ping within your configured grace period, it emails you (or sends a Slack/webhook notification).
For scheduled workflows (on: schedule:), add a final step:
- name: Ping heartbeat
if: success()
run: curl -fsS https://hc-ping.com/your-uuid-here
on: schedule: for anything critical, add a heartbeat so you'll notice when this happens.
Instead of modifying every individual cron job, wrap your jobs in a shell function that captures exit codes and fires notifications on failure. This approach works well when you have many jobs and don't want to change each one.
#!/bin/bash
# /usr/local/bin/cron-wrapper
# Usage: cron-wrapper "Job Name" /path/to/job.sh
JOB_NAME="$1"
shift
"$@"
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
MESSAGE="CRON FAILED: ${JOB_NAME} exited with code ${EXIT_CODE} at $(date)"
# Option 1: Email (requires mailutils)
echo "$MESSAGE" | mail -s "Cron failure: ${JOB_NAME}" you@example.com
# Option 2: Slack webhook
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-type: application/json' \
-d "{\"text\":\"$MESSAGE\"}"
fi
exit $EXIT_CODE
Then update your crontab to use the wrapper:
0 9 * * 1-5 /usr/local/bin/cron-wrapper "Daily report" /path/to/report.sh
0 2 * * * /usr/local/bin/cron-wrapper "Nightly cleanup" /path/to/cleanup.sh
This catches exit-code failures but doesn't detect the job failing to run at all. Combine it with the heartbeat approach for full coverage.
If your jobs already write to log files, you can monitor the logs rather than the jobs themselves. There are two useful patterns:
Tools like Papertrail, Datadog, Logtail, or self-hosted Grafana Loki can alert when log lines match patterns like ERROR, FATAL, or Traceback. Set up an alert that fires within a few minutes of a matching line appearing.
More subtle: alert when a job that should have logged something doesn't. Most log monitoring tools support "absence alerts" — fire if no log line matching "daily-report: completed" appears between 9:00am and 9:30am.
# In your job script, log a completion marker:
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) daily-report: completed rows_processed=${ROW_COUNT}"
Then configure your log monitoring to alert if that marker doesn't appear on schedule.
If you have more than a handful of scheduled jobs, managing heartbeat URLs, wrappers, and log alerts becomes overhead in itself. Dedicated cron monitoring services handle all of this with a single integration:
healthchecks.io — open source, free tier (20 checks), solid reliability. Good for most indie use cases.
Cronitor — more polished UI, better dashboards, paid from $10/mo. Worth it if you're monitoring 10+ jobs for a production product.
Dead Man's Snitch — dead simple, email-only on free tier, $5/mo for more. Very low friction to set up.
CronWatch (early access) — monitoring across crontab, GitHub Actions, Heroku Scheduler, and AWS EventBridge from a single dashboard. Designed for indie developers with jobs spread across multiple platforms.
For a single critical job with no existing infrastructure: dead man's switch. One extra line in your crontab, five-minute setup, catches both failure modes.
For a large number of jobs on a single server: wrapper script + heartbeat for critical jobs.
For jobs spread across platforms (crontab, GitHub Actions, Heroku, AWS): dedicated service. The overhead of managing separate integrations per platform adds up fast.
One dashboard for crontab, GitHub Actions scheduled workflows, Heroku Scheduler, and AWS EventBridge. Get alerted the moment a job misses its window — without setting up separate integrations for each platform.
Join the CronWatch waitlist → Free cron expression parserPick one and do it today:
&& curl -fsS https://hc-ping.com/YOUR_UUID to the end of that job's crontab entryThe first one takes 10 minutes. After that it's mostly copy-paste. If you have more than five jobs or jobs on multiple platforms, CronWatch handles it from a single place.
Not sure what your cron expression actually does? Parse it here to see the plain-English description and next run times before you monitor it.