# Cron Job Monitoring With Heartbeats: Get Called When a Job Doesn't Run

> Monitor cron jobs with a heartbeat URL: add a curl ping to crontab, report failures with ?status=fail, and get a phone call when a job doesn't run.

Source: https://edesy.in/blog/cron-job-monitoring-heartbeat-2026

**Cron job monitoring with heartbeats works like a dead man's switch: your job pings a unique URL every time it succeeds, and if the ping doesn't arrive on time, someone gets a phone call.** With Edesy OnCall you create a Heartbeat monitor, copy its URL, add one `curl` line to your crontab, and set how often the ping should arrive. Miss the window, or report a failure, and the escalation policy starts calling.

Uptime checks can't catch this class of failure. Your website is up, your server is up, but the nightly backup stopped three weeks ago because a disk filled up or a password expired. Nothing errors. Nothing alerts. A heartbeat monitor watches for the *absence* of a signal.

## How a heartbeat monitor decides a job failed

| Event | What OnCall does |
|---|---|
| Ping arrives on time | Monitor stays up; the period restarts |
| No ping within period + grace | Incident opens: "no heartbeat received for over 1 hour" |
| Ping with `?status=fail` | Incident opens immediately, with your message |
| Next good ping after a failure | Incident resolves; paged contacts get a "back up" email |

Two settings matter:

- **Period (interval):** how often you expect a ping. It must match your schedule: a job that runs hourly has a 1-hour period. The shortest period follows your plan (5 minutes on Free, 60 seconds on Starter, 30 seconds on Pro) and the longest is 24 hours.
- **Grace:** extra time allowed before the ping counts as late, from 0 seconds to 24 hours (default 2 minutes). Set it a bit longer than your job usually takes to run, because the ping only fires when the job finishes.

## Step 1: Create the monitor

In [oncall.edesy.in](https://oncall.edesy.in), go to **Monitors → New**, choose **Heartbeat**, name it ("Nightly DB backup"), set the period and grace, and pick an escalation policy. The monitor page shows its URL:

```
https://oncall.edesy.in/hb/<token>
```

The token is a secret. Anyone who has the URL can send pings to it, so keep it out of public repos. If it leaks, rotate the token on the monitor page and update your crontab.

## Step 2: Add the ping to your crontab

Ping **after** the job succeeds, chained with `&&`:

```bash
# m h dom mon dow  command
*/5 * * * *  /usr/local/bin/backup.sh && curl -fsS -m 10 --retry 3 https://oncall.edesy.in/hb/<token> > /dev/null
```

What the flags do:

- `-f` makes curl fail on HTTP errors.
- `-sS` hides the progress bar but still shows errors.
- `-m 10` stops curl after 10 seconds, so a network problem can't hang your job.
- `--retry 3` retries a dropped ping, so a brief network blip doesn't page anyone.

Because of `&&`, a failing `backup.sh` sends no ping, and the missed window raises the alert. Need a cron expression? The [Cron Expression Builder](https://edesy.in/tools/cron-job-monitor) shows the next run times for any schedule, and the [cron heartbeat URL generator](https://edesy.in/tools/cron-heartbeat-url-generator) writes the full crontab line for you.

## Step 3: Report failures immediately (optional)

Waiting for a missed ping can mean waiting a whole period. To get called the moment a job fails, report it:

```bash
/usr/local/bin/backup.sh || curl -fsS -m 10 "https://oncall.edesy.in/hb/<token>?status=fail&msg=backup%20failed"
```

Or wrap it in a script that covers both cases:

```sh
#!/bin/sh
if /usr/local/bin/backup.sh; then
  curl -fsS -m 10 --retry 3 https://oncall.edesy.in/hb/<token> > /dev/null
else
  curl -fsS -m 10 "https://oncall.edesy.in/hb/<token>?status=fail&msg=exit%20code%20$?"
fi
```

The `msg` text (up to 200 characters) becomes the reason in the incident, the email and the call: *"Nightly DB backup is down since 2:05 AM. Reason: backup failed."* `status=down` works the same as `status=fail`.

One crontab gotcha: cron treats a `%` in a command line as a newline. Put URL-encoded messages in a wrapper script, as above, or escape each `%` as `\%` inside crontab.

## What to monitor with heartbeats

| Job | Typical period | Grace |
|---|---|---|
| Database backup | 24 hours | Backup run time + margin |
| Queue worker loop | 5 minutes | 2 minutes |
| Invoice or report generation | 24 hours | 30 minutes |
| Data sync from a partner API | 1 hour | 10 minutes |
| Certificate or log rotation script | 24 hours | 1 hour |

Heartbeats work for anything that can make an HTTP request: cron, systemd timers, Kubernetes CronJobs, CI schedules, a Windows scheduled task, or a long-running worker that pings every few minutes. The endpoint accepts any HTTP method and is rate-limited to 120 requests a minute per IP, which is far more than any sane heartbeat needs.

The longest window is 24 hours plus 24 hours of grace. A weekly job can't be watched directly; add a daily ping step instead.

## Who gets the call

The heartbeat monitor uses an escalation policy like any other monitor: call the job's owner first, then a backup after a few minutes, with email alongside. The call is spoken in English or Hindi, and the person presses 1 to acknowledge or 2 to escalate. See [escalation policies explained](https://edesy.in/blog/on-call-escalation-policy-explained-2026).

## Cost

Heartbeat monitors count towards your plan's monitor limit: 3 on Free, 20 on Starter (Rs 499/month), 100 on Pro (Rs 1,499/month). Alert calls are billed per minute from your Edesy wallet. Prices exclude 18% GST. See [pricing](https://edesy.in/uptime-monitoring-phone-call-alerts/pricing). OnCall needs an Edesy phone number in your workspace as the caller ID.

More: [heartbeat monitor docs](https://edesy.in/docs/oncall/monitors/heartbeat), the [cron job failure alert use case](https://edesy.in/uptime-monitoring-phone-call-alerts/use-cases/cron-job-failure-alert) and the [OnCall overview](https://edesy.in/uptime-monitoring-phone-call-alerts).

## FAQ

### What is heartbeat monitoring for cron jobs?

Your job calls a unique URL each time it finishes. The monitor expects that ping every period. If it doesn't arrive within the period plus a grace time, the job is treated as failed and the people on your escalation policy are phoned.

### How do I report a failure right away?

Call the heartbeat URL with ?status=fail and an optional msg, for example when your script exits with an error. OnCall opens an incident immediately instead of waiting for the ping to go missing.

### Can I monitor a weekly job?

Not directly. The heartbeat period can be at most 24 hours, plus up to 24 hours of grace. For a weekly job, add a daily ping step or monitor the job's result another way.

### Does OnCall run my cron jobs?

No. Your server keeps running the jobs. OnCall only listens for the pings and calls you when one is missing or reports a failure.

**Create your first heartbeat monitor free at [oncall.edesy.in](https://oncall.edesy.in).**
