Guide

Monitor cron jobs with heartbeats

Goal

Get an alert when a backup or scheduled job stops running, whether it runs from cron, a systemd timer, GitHub Actions or Kubernetes.

A backup that stops running makes no noise, and you find out when someone asks for a restore. A heartbeat flips that around. Your job reports in when it finishes, and OctoPing sends you an alert when a report doesn't arrive on time. This guide sets one up and wires it into cron.

What you need

  • An OctoPing account with a verified email address, signed in as a Member, Admin or Owner.
  • A job that runs on a schedule and a way to edit the command it runs.
  • curl on the machine or in the container image that runs the job.

Create the heartbeat and wire up the job

  1. Add the heartbeat

    Open Heartbeats and choose Add Heartbeat. Enter a Name such as Nightly backup, and a Description if you want one in the alert. Set Expected Interval to the longest normal gap between two runs. For a nightly job, choose Every 1 day. Set Grace Period to the extra time a slow run may take, then choose Create Heartbeat Monitor.

  2. Copy the ping URL

    The heartbeat's page shows a Ping URL card. The last part of the address is the secret key. Anyone who has it can send pings for this heartbeat, so keep it in a secrets store or a file only your job can read, and out of public repositories.

  3. Test the URL by hand

    Run the request once from the machine that will run the job. The reply is JSON, and ok is true when the ping was recorded.

    A recorded ping (the id and time are examples)
    $ curl -fsS https://octoping.com/api/ping/YOUR_SECRET_KEY
    {"ok": true, "message": "Ping received", "heartbeat_id": "00000000-0000-0000-0000-000000000000", "received_at": "2026-11-07T04:52:41.198Z"}
  4. Ping only when the job succeeds

    Chain the request after the job with &&. The shell then skips the ping when the job exits with an error, and the heartbeat goes late. A job that crashes gets reported without any extra code.

    The pattern in one line
    ./backup.sh && curl -fsS https://octoping.com/api/ping/YOUR_SECRET_KEY
  5. Check that OctoPing recorded the ping

    OctoPing answers HTTP 200 even when it refuses a ping, so curl -fsS exits successfully for a mistyped key. The body gives it away. Put the request in a small script that fails unless the response says ok is true. Save it as ping-octoping.sh and make it executable.

    ping-octoping.sh
    #!/bin/sh
    # ping-octoping.sh: tell OctoPing the job finished, and fail if it didn't hear us.
    set -eu
    curl -fsS --retry 3 --max-time 10 "${PING_URL:?set PING_URL}" | grep -q '"ok": *true'

With a wrong key, the answer is HTTP 200 with a body of {"ok": false, "message": "Invalid ping URL", "heartbeat_id": null, "received_at": null}. The script above prints nothing and exits with status 1, so the job's own log shows the problem. The other refusals, such as a disabled heartbeat, read the same way. They're listed on the heartbeat ping reference.

Schedule it in cron

Set the URL once at the top of the crontab and chain the script after the job. The ping runs after the job succeeds and not before.

crontab -e
PING_URL=https://octoping.com/api/ping/YOUR_SECRET_KEY
0 2 * * * /usr/local/bin/backup.sh && /usr/local/bin/ping-octoping.sh

Verify it works

  • Run the job once by hand. The heartbeat's page lists the ping under Recent Pings with its time, source address and user agent.
  • Make the job fail on purpose, for example by pointing the command at a script that exits with status 1. No ping arrives. Once the Expected Interval and the Grace Period have passed, OctoPing counts a missed check every minute, and the alert goes out when the count reaches the notification's Retry Threshold. The default is 3, so the late email arrives about 3 minutes after the deadline. The subject reads 🔴 Alert: Heartbeat 'Nightly backup' is late, and the body gives the last ping time and the number of missed checks.
  • Run the job again after a successful fix. The first ping marks the heartbeat healthy and sends the recovery alert.

Report how the run went

A POST can carry a JSON body of up to 10240 bytes. OctoPing stores it with the ping, and you can read it under Recent Pings. It's a handy place for the duration or the row count of a run.

bash
./backup.sh && curl -fsS -X POST \
  -H "Content-Type: application/json" \
  -d '{"job":"nightly-backup","rows":1204}' \
  https://octoping.com/api/ping/YOUR_SECRET_KEY

Read the reference for Heartbeat ping

Last reviewed

Stop finding out last.

Add a site, pick which of the eight checks matter, and get on with your day. The trial is free for 14 days and needs no card.