Scheduled pings

The ping patterns for jobs that run on a timetable, from the one-liner to the fully wired version.

The shape to reach for first:

bash
API="https://api.drumbeats.io/v1/ping/<monitor-id>"

curl -sf "$API/start"

if /usr/bin/backup.sh; then
  curl -sf "$API/success"
else
  curl -sf "$API/failure"
fi
API="https://api.drumbeats.io/v1/ping/<monitor-id>"

curl -sf "$API/start"

if /usr/bin/backup.sh; then
  curl -sf "$API/success"
else
  curl -sf "$API/failure"
fi

Two beats per run. You get duration tracking, immediate failure alerts, and hung-run detection.

These patterns apply to Cron and Heartbeat monitors, the two types that expect a ping on a timetable.

Start with one ping if that is all you need#

One ping at the end covers missed-run detection and costs 1 beat per run:

bash
/usr/bin/backup.sh
curl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/success
/usr/bin/backup.sh
curl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/success

Two gaps. A non-zero exit still sends success, because nothing checks the exit status. And under set -e, or if the process is killed, the curl never runs at all and Drumbeats hears nothing until the window closes. Either way you learn about the failure at the next missed window rather than when it happened.

Add the failure branch as soon as the job matters.

Write it inline in crontab#

No wrapper script needed:

bash
0 2 * * * curl -sf https://api.drumbeats.io/v1/ping/<id>/start \
  && /usr/bin/backup.sh \
  && curl -sf https://api.drumbeats.io/v1/ping/<id>/success \
  || curl -sf https://api.drumbeats.io/v1/ping/<id>/failure
0 2 * * * curl -sf https://api.drumbeats.io/v1/ping/<id>/start \
  && /usr/bin/backup.sh \
  && curl -sf https://api.drumbeats.io/v1/ping/<id>/success \
  || curl -sf https://api.drumbeats.io/v1/ping/<id>/failure

Always use curl -sf inside crontab. -s silences the progress meter, which cron would otherwise mail to root on every run. -f makes curl exit non-zero on an HTTP error, so a 500 from the ping API does not read as success to the rest of your chain.

Use the exit code when the branch is binary#

bash
/usr/bin/backup.sh
curl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/$?
/usr/bin/backup.sh
curl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/$?

0 becomes success, anything else becomes failure, and the exact code is stored for triage. Exit codes has the full table and the concurrency-safe variants.

Send the error output with the failure#

The useful payload is the tail of stderr, not the whole log:

bash
OUTPUT=$(run-job.sh 2>&1 | tail -n 100)
EXIT=$?

if [ $EXIT -eq 0 ]; then
  curl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/success
else
  curl -sf -X POST https://api.drumbeats.io/v1/ping/<monitor-id>/failure \
    -H "Content-Type: application/json" \
    -d "$(jq -n --arg p "$OUTPUT" '{payload: $p}')"
fi
OUTPUT=$(run-job.sh 2>&1 | tail -n 100)
EXIT=$?

if [ $EXIT -eq 0 ]; then
  curl -sf https://api.drumbeats.io/v1/ping/<monitor-id>/success
else
  curl -sf -X POST https://api.drumbeats.io/v1/ping/<monitor-id>/failure \
    -H "Content-Type: application/json" \
    -d "$(jq -n --arg p "$OUTPUT" '{payload: $p}')"
fi

Build the JSON with jq. A stack trace containing a quote or a newline breaks hand-rolled string interpolation, and it breaks on exactly the runs where you needed the payload.

Record which phase a job reached#

bash
API="https://api.drumbeats.io/v1/ping/<monitor-id>"

curl -sf "$API/start"

pg_dump mydb > backup.sql \
  || { curl -sf "$API/failure"; exit 1; }

curl -sf -X POST "$API/log" -H "Content-Type: application/json" \
  -d '{"payload":"DB dump complete (1.2 GB)"}'

gzip backup.sql \
  || { curl -sf "$API/failure"; exit 1; }

aws s3 cp backup.sql.gz s3://backups/ \
  || { curl -sf "$API/failure"; exit 1; }

curl -sf "$API/success"
API="https://api.drumbeats.io/v1/ping/<monitor-id>"

curl -sf "$API/start"

pg_dump mydb > backup.sql \
  || { curl -sf "$API/failure"; exit 1; }

curl -sf -X POST "$API/log" -H "Content-Type: application/json" \
  -d '{"payload":"DB dump complete (1.2 GB)"}'

gzip backup.sql \
  || { curl -sf "$API/failure"; exit 1; }

aws s3 cp backup.sql.gz s3://backups/ \
  || { curl -sf "$API/failure"; exit 1; }

curl -sf "$API/success"

Each log costs a beat. Use them on jobs where knowing the phase changes what you do next.

What happens when it breaks#

What Drumbeats seesResult
successMonitor flips UP, any open incident resolves
failureFailure counter increments. Monitor flips DOWN at failure_tolerance
start with no finish inside max_duration_secondsHung run, counted as a failure
logStored on the timeline. No status change
Nothing by the expected time plus grace_period_secondsMISSED incident opens
Finish slower than max_duration_secondsDURATION_HIGH warning. The monitor stays UP
Finish faster than min_duration_secondsDURATION_LOW warning. The monitor stays UP

How you get alerted#

The incident pages every notification group on the monitor, and all channels fire in parallel. The next success resolves it and sends a recovery message carrying the outage duration.

Do not let the ping break the job#

If a ping outage should never fail the underlying work, swallow the error:

The real work already happened. Losing one ping costs you a data point. Letting a monitoring call abort a backup costs you the backup. Always pair || true with --max-time so a hung connection cannot stall the job either.

Next#

Cron monitors and heartbeat monitors for what the monitor does with these pings. Payloads for size limits and truncation. Production hardening for retries and timeouts.