Heartbeat Monitoring

Heartbeats are passive monitors for cron jobs, scheduled workers, and batch pipelines. Instead of Upwatch probing your service, your job pings Upwatch when it completes. Silence past the expected interval triggers an alert.

favorite

Dead-man's switch

If your cron job stops checking in, we page you. No agents. No polling. Just a curl at the end of your script.

timer

Flexible intervals

From 60 seconds to 7 days. Set a grace period to absorb natural jitter. Free plan: 10 heartbeats. Pro: unlimited.

Quick Start

1

Create a heartbeat

In your dashboard, go to Heartbeats and click New Heartbeat. Give it a name (e.g. "Nightly DB backup"), set the expected interval, and optionally a grace period.

2

Copy your ping URL

After creation, you'll see your unique ping URL:

https://upwatch.dev/ping/YOUR_TOKEN
3

Append the ping to your job

Add the curl call at the end of your cron job or script:

# your job runs first, then pings upwatch on success
0 3 * * * /opt/scripts/backup.sh && curl -fsS https://upwatch.dev/ping/TOKEN > /dev/null

Ping Endpoints

All three HTTP methods are accepted on the success endpoint, so you can use whatever is most convenient for your runtime.

GETPOSTHEAD
/ping/{token}

Records a successful check-in. Resets the incident timer. If an active incident was open, it resolves automatically and sends an "all clear" alert.

curl -fsS https://upwatch.dev/ping/TOKEN
POST/ping/{token}/fail

Explicitly reports a failure. Opens an incident immediately without waiting for the grace period. Use this when your script detects an error and can report it proactively.

#!/bin/bash
/opt/scripts/backup.sh
if [ $? -ne 0 ]; then
curl -fsS -X POST https://upwatch.dev/ping/TOKEN/fail
exit 1
fi
curl -fsS https://upwatch.dev/ping/TOKEN

Note: The ping URL does not require authentication. Treat it like a secret — don't commit it to public repos. Rotate it by deleting and recreating the heartbeat if compromised.

Code Examples

crontab

# Ping upwatch when backup succeeds (using &&)
0 3 * * * /opt/scripts/backup.sh && curl -fsS https://upwatch.dev/ping/TOKEN >/dev/null

# Use -m 10 to enforce a 10-second timeout on the ping itself
0 3 * * * /opt/scripts/backup.sh && curl -fsS -m 10 https://upwatch.dev/ping/TOKEN >/dev/null

Python

import urllib.request

def run_backup():
# ... your backup logic ...
pass

try:
run_backup()
urllib.request.urlopen("https://upwatch.dev/ping/TOKEN", timeout=10)
except Exception as e:
urllib.request.urlopen("https://upwatch.dev/ping/TOKEN/fail", timeout=10)
raise

GitHub Actions

jobs:
scheduled-task:
runs-on: ubuntu-latest
steps:
- name: Run backup
run: ./scripts/backup.sh
- name: Ping heartbeat
if: success()
run: curl -fsS https://upwatch.dev/ping/${{ secrets.UPWATCH_HB_TOKEN }}

Store the token in a GitHub Actions secret to avoid exposing it in logs.

wget

wget -q -O /dev/null https://upwatch.dev/ping/TOKEN

Intervals & Grace Periods

Expected Interval

How often your job should check in. Range: 60 seconds to 7 days. If no ping arrives within this window after the last ping (or creation for new heartbeats), the grace period begins.

Grace Period

Extra buffer beyond the interval before an incident opens. A 5-minute grace on an hourly job means we wait until 65 minutes before alerting. Use this to absorb natural jitter from system load or network latency.

When does the clock start?

For a new heartbeat that has never been pinged, the interval is measured from the heartbeat's creation time. Once it receives its first ping, the clock resets to that ping's timestamp and runs from there.

Best Practices

check_circle

Ping on success, not on start

Use job.sh && curl /ping/TOKEN so the ping only fires when the job actually completes without error.

check_circle

Add -m 10 to curl

A network hiccup on the ping call shouldn't hang your job indefinitely. -m 10 enforces a 10-second max wait.

check_circle

Store the token as a secret or env var

Don't hardcode it in scripts committed to public repos. Use UPWATCH_HB_TOKEN in your environment or a secrets manager.

check_circle

Use one heartbeat per job, not per machine

If you run the same backup on three servers, create three heartbeats. Shared tokens make it impossible to tell which machine stopped checking in.

check_circle

Set grace = natural jitter × 2

If your job usually takes 5–10 minutes, set a 20-minute grace. You want alerts for real failures, not for load-induced delays.

FAQ

Does the ping URL expire?

No. Tokens are permanent until you delete the heartbeat. If you suspect a token has been compromised, delete the heartbeat and create a new one.

What's the rate limit on pings?

120 requests per token per 60 seconds. This is more than enough for any reasonable job frequency and is designed to absorb retry loops.

Can I use GET or only POST?

Both GET and POST (and HEAD) are accepted on the success endpoint. Use whatever your runtime supports most easily. The /fail endpoint only accepts POST.

How is heartbeat monitoring different from HTTP monitoring?

HTTP monitors poll a URL on a schedule — they test if something is reachable. Heartbeat monitors wait for a ping — they test if something ran. You need both for complete coverage: HTTP for services that should always be up, heartbeats for jobs that should run periodically.