Cron-Ping
InloggenGratis beginnen

Alert when your backup stops running — before you need it

Last updated: July 2026 · By Yohann Kipfer, Cron-Ping

In short

The dangerous backup failure isn't « the dump errored ». It's « the dump never even started — for weeks — and nobody knew ». A full disk, an expired SSH key, a crontab wiped by a server rebuild: the backup silently stops, and you find out the day you try to restore. Add a ping that fires only when the backup succeeds, and let its absence alert you:

0 2 * * * /opt/backup.sh && curl -fsS https://cron-ping.com/p/TOKEN

The && is the whole trick: curl runs only if backup.sh exits 0. If the script fails or never runs at all, no ping reaches Cron-Ping, and when the grace period passes you get an email. A backup that isn't monitored isn't a backup — it's a hope.

The nightmare scenario is silence, not an error

Most teams that lose data didn't have « no backups ». They had backups that had been silently broken for months. The classic chain: the backup target disk fills up, or the offsite SSH key expires, or a server rebuild drops the crontab line — and the nightly job either errors into /dev/null or never fires. Everything looks normal. The dashboards are green. Then a disk dies, someone runs the restore, and the newest usable copy is from before the problem started.

It has happened at scale. In January 2017 GitLab lost ~6 hours of production database data during an incident where, in their own post-mortem, five separate backup and replication methods were found to be failing or not working as intended — silently. They recovered only from a manual snapshot a staff engineer had taken by luck. The lesson they wrote down: an untested, unmonitored backup is indistinguishable from no backup.

A monitor doesn't make backups; it makes their absence loud. That's the missing half of every backup script.

Ping on success — one line per tool

Chain the ping with && so it only fires on a clean exit (code 0). Here's the pattern for the common tools:

Adding a success ping to common backup commands
ToolLine
mysqldumpmysqldump --single-transaction db | gzip > d.sql.gz && curl -fsS …/p/TOKEN
pg_dumppg_dump -Fc db > d.dump && curl -fsS …/p/TOKEN
rsyncrsync -a /data/ backup:/srv/ && curl -fsS …/p/TOKEN
borgborg create ::'{now}' /data && curl -fsS …/p/TOKEN
resticrestic backup /data && curl -fsS …/p/TOKEN

curl -fsS is deliberate: -f fails on an HTTP error, -s is silent, -S still shows an error if something breaks — so the curl itself won't quietly fail. Give the Cron-Ping check the same period as the backup (daily → 1 day, weekly → 7 days) and a grace period generous enough to cover a slow night without hiding a truly missed run.

A clean exit code is not a good backup — check the size

mysqldump can exit 0 and still hand you a broken dump. Two real traps: with --force it keeps going past a table error and still exits 0, and a dump interrupted by the connection dropping can leave a truncated file with a 0 exit if buffering hides it. The fix is to verify the artifact, not just the exit code — and send /fail when it's empty:

#!/bin/bash
set -o pipefail
OUT=/backup/db-$(date +%F).sql.gz
mysqldump --single-transaction --quick app | gzip > "$OUT"

# a real backup is never a few bytes — 0-byte or tiny = failure
if [ -s "$OUT" ] && [ "$(stat -c%s "$OUT")" -gt 1024 ]; then
  curl -fsS https://cron-ping.com/p/TOKEN
else
  curl -fsS https://cron-ping.com/p/TOKEN/fail
fi

set -o pipefail matters here: without it, mysqldump | gzip returns gzip's exit code, so a failed dump piped into a successful gzip looks like success. With pipefail, the pipeline fails if any stage fails.

Measure the run, and read the tool's own exit codes

Ping /start at the top and /success at the end, and Cron-Ping records the *duration*. A nightly pg_dump that has crept from 4 minutes to 40 is telling you the database grew — useful long before it becomes a failed 3 a.m. job. And borg and restic have richer exit codes than 0/1 that are worth branching on:

borg and restic exit codes worth acting on
Codeborgrestic
0successsuccess
1warning (backup completed with warnings)general error — backup failed
2error — backup failedGo runtime error
3some source files could not be read (partial backup)

A borg exit of 1 (warning) or a restic exit of 3 (partial) is exactly the « ran, but not really » state that a plain && would count as success. Branch on the code and send /fail so a partial backup doesn't masquerade as a good one.

The rest of the insurance: 3-2-1, encryption, and testing the restore

FAQ

How do I get an alert if my backup stops running?

Add && curl -fsS https://cron-ping.com/p/TOKEN to the end of the backup command and create a check whose period matches the schedule. The ping only fires on a successful backup; if the backup fails or never runs, no ping arrives, and once the grace period elapses Cron-Ping emails or Slacks you. That covers the case internal tools can't: a job that never started.

My backup script exits 0 but the dump is empty — how do I catch that?

Check the artifact, not just the exit code. Test the file size ([ -s file ] and a minimum byte count) before pinging success, and hit the /fail endpoint when it's too small. Also use set -o pipefail so a failed mysqldump | gzip doesn't report gzip's success as the whole pipeline's.

What period and grace should I set for a nightly backup?

Period = 1 day for a nightly job, 7 days for a weekly one. Grace has to cover a normal run's duration plus a bit of slack — if the dump usually takes 20 minutes, a 1-2 hour grace absorbs a slow night without hiding a run that genuinely didn't happen. For a weekly backup a grace of several hours to a day is reasonable.

Does this work with restic and borg?

Yes. Chain the ping after restic backup or borg create. Better still, branch on their exit codes: a borg exit of 1 (warning) or a restic exit of 3 (some files unreadable) is a partial backup you want to treat as a failure and send to /fail, not count as green.

Isn't a green backup log enough?

No — a log only exists if the job ran, and you'd have to be reading it. The failures that cost people data are the ones where the job silently stopped: a wiped crontab, a full disk, an expired key. There's no error log for a job that never fired. A missed heartbeat is the only signal that reaches you without you looking.

Get an email the day your backup stops — not the day you need it

Add one && curl to your backup script and set a check to its schedule. If the dump fails, runs empty, or never starts, the ping doesn't arrive and Cron-Ping alerts you — while there's still a good copy to fall back on.

Create a free check

Free plan: 10 checks, email alerts, 7-day history. No card. EU-hosted.

Related

Monitoring a Linux cron job that stoppedThe OS layer under your backup cron: crond, /var/mail, a clobbered crontab.Monitoring the Laravel schedulerIf your backup runs as a scheduled Laravel task, watch the scheduler too.Supabase cron monitoringA backup running from pg_cron? Ping out with pg_net the same way.Cron expression validatorCheck the schedule of your backup line actually fires when you think it does.

Sources