← Blog  ·  October 2026

Cron Job Heartbeat Monitor: Know Instantly When Your Scheduled Jobs Stop Running

Cron jobs fail silently — no output, no error, no alert. Heartbeat monitoring flips the model so you find out immediately when a job stops running, not days later when the damage is done.

Scheduled jobs are the silent backbone of most production systems: database backups, report generation, cache warming, cleanup scripts, billing runs, queue processors. When they work, nobody thinks about them. When they stop working, the failure is invisible — there is no exception to catch, no HTTP 500 to log, no user complaint to triage. A cron job heartbeat monitor detects that silence and turns it into an alert.


Why cron job failures are so hard to detect

Most application monitoring tools are reactive. They watch for errors your code raises, HTTP status codes your server returns, or thresholds your metrics breach. A cron job that stops running raises none of these signals — it simply produces no output and generates no activity.

No output means no error logging

By default, cron sends failed job output to the system mail spool of the user running the job. On most production servers, that mail spool goes unread indefinitely. Even when output is redirected to a log file, the log being empty is not distinguishable from the log being correct — a job that ran successfully also produces no output in many cases.

Cron itself doesn't know if a job "worked"

Cron only tracks whether a process was spawned and what exit code it returned. If your backup script exits 0 but produces a zero-byte archive, cron considers the job successful. If your report generation script exits 0 but fails to write the output because a database query timed out and the error was swallowed, cron considers the job successful.

Jobs can stop being scheduled without notice

Crontab files disappear during server reimaging. Package upgrades can overwrite systemd timer units. Configuration management tools can reset cron directories. A user-level crontab that worked for 18 months can silently vanish during a routine update, and the absence of a job running is indistinguishable from a job that ran but produced no output.

The core problem: absence of evidence vs. evidence of absence. Standard monitoring waits for a bad signal. Cron job heartbeat monitoring checks for the expected good signal — and alerts when it doesn't arrive.

What heartbeat monitoring is

Heartbeat monitoring inverts the detection model. Instead of your monitoring system polling your job to check if it ran, your job pings your monitoring system after it runs. If no ping arrives within the expected window, the monitoring system fires an alert.

The implementation is minimal. At the end of your cron job, add a single curl or wget call to a unique URL assigned to that job. The monitoring service records the timestamp. If the URL isn't pinged within the expected interval — the job's period plus a grace window — you receive an alert.

This approach catches four distinct failure modes in one mechanism:


Implementing heartbeat pings in your cron jobs

Basic curl ping

The simplest implementation: append a curl call to your existing cron job. The && operator ensures the ping only fires on success:

# In crontab: run backup every day at 2am, ping on success
0 2 * * * /opt/scripts/backup.sh && curl -fsS --retry 3 https://cronwatch.example/ping/YOUR-JOB-ID

The -fsS flags make curl fail silently on HTTP errors (-f), suppress progress output (-s), but still show errors (-S). The --retry 3 handles transient network issues without blocking your job.

Wrapping the full job

For a job script you own, call the ping from inside the script after verifying the result — not merely that the process exited 0:

#!/bin/bash
set -euo pipefail

# Your actual job logic
pg_dump mydb | gzip > /backups/mydb-$(date +%Y%m%d).sql.gz

# Verify the output exists and is non-empty before pinging
BACKUP_FILE="/backups/mydb-$(date +%Y%m%d).sql.gz"
if [[ -f "$BACKUP_FILE" && -s "$BACKUP_FILE" ]]; then
  curl -fsS --retry 3 "https://cronwatch.example/ping/YOUR-JOB-ID?bytes=$(stat -c%s "$BACKUP_FILE")"
else
  echo "Backup file missing or empty — not pinging heartbeat" >&2
  exit 1
fi

systemd OnSuccess handler

For jobs managed as systemd services, use the OnSuccess directive to fire a ping unit only when the service exits cleanly:

[Unit]
Description=Daily database backup

[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
OnSuccess=backup-heartbeat.service
# backup-heartbeat.service
[Unit]
Description=Heartbeat ping for daily backup

[Service]
Type=oneshot
ExecStart=curl -fsS https://cronwatch.example/ping/YOUR-JOB-ID

Setting the right grace window

The grace window is how long after the expected schedule the monitoring service waits before alerting. A 10-minute job scheduled hourly might use a 15-minute grace window. A 2-hour batch job running nightly might use a 30-minute grace window. Too tight a grace window produces false positives; too loose and you find out about failures hours late. Start with 10–20% of the job's typical duration and adjust based on observed variance.


Why purpose-built tools beat DIY heartbeat setups

You can build a basic heartbeat receiver yourself — a web endpoint that records pings, a background process that checks for missed pings, a script that sends an email. Developers do this regularly. The problems emerge at scale and over time:

A purpose-built cron job heartbeat monitor runs independently of your infrastructure, stores job history, handles alert routing, and gives you a dashboard to see all your scheduled jobs in one place. You focus on what the jobs do; the monitor handles whether they ran.

To validate your cron expression before wiring up monitoring, use the free CronWatch expression parser — paste any cron schedule and it shows you the next 10 run times in plain English, making it easy to verify your schedule is correct before you depend on it.


Summary

Cron jobs fail silently, and standard monitoring tools are blind to silence. Heartbeat monitoring solves this by having each job ping a URL on completion — if the expected ping doesn't arrive within the schedule window plus a grace period, you get an alert. The implementation is a single curl call appended to your existing job. For production reliability, a purpose-built cron job heartbeat monitor runs outside your infrastructure and handles the alert routing and history that a homegrown solution requires you to build and maintain yourself.

Stop finding out about cron failures days later

CronWatch monitors your scheduled jobs with heartbeat pings and alerts you the moment a job stops running. No agents to install — just add a curl call to your existing jobs.

Join the waitlist at https://lior-seats.github.io/cronwatch-waitlist/

Join the CronWatch Waitlist Parse Your Cron Expression (Free)

© 2026 Developer Tools Blog