How to monitor a database backup cron job for missed runs
To confirm a database backup cron job actually completed, don't rely on cron's built-in email — it only mails command output when there is output, and sends nothing if cron itself never runs the job. The reliable approach is heartbeat monitoring: have the backup script ping an external monitor as its final action on success, so a missed ping — whether from a failed backup, a down machine, or a removed crontab entry — alerts you before anyone needs the backup.
Why can't cron's MAILTO tell me the backup was missed?
MAILTO routes whatever the backup job prints to stdout/stderr. A job that never runs produces no output, so there is nothing to mail. The critical failure mode for backups is a job that silently stopped days or weeks ago — and the gap isn't discovered until a restore is needed.
How do I add a heartbeat to a database backup script?
#!/usr/bin/env bash
set -euo pipefail
BACKUP_DIR="/backups"
TODAY=$(date +%Y%m%d)
FILE="$BACKUP_DIR/db_$TODAY.sql.gz"
# Dump and compress. set -e aborts on any non-zero exit.
pg_dump -Fc "$DATABASE_URL" | gzip > "$FILE"
# Verify the backup file is non-empty (a connection error produces an empty file).
[ -s "$FILE" ] || { echo "backup empty"; exit 1; }
# Delete backups older than 30 days.
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +30 -delete
# Only reached on full success.
curl -fsS -m 10 --retry 3 "https://ping.cronshield.com/<your-check-id>"# Run daily at 02:00. Output to log file (MAILTO also works if MTA is configured).
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1How do I confirm recent backup files actually exist?
# Check that a backup file for today exists and is non-empty.
ls -lh /backups/db_$(date +%Y%m%d).sql.gz
# Check the most recent backup age (alert if older than 1 day).
find /backups -name "*.sql.gz" -newer /backups/db_$(date +%Y%m%d -d "1 day ago").sql.gz | wc -lAn external heartbeat confirms the script ran; file-existence checks confirm the output is there. Both together give you defense in depth for backup monitoring.
Add a missed-run alert to this job
The free tier gives you a heartbeat endpoint and an email alert when an expected ping doesn't arrive. Paid tiers add the log-aware diagnosis — the last log line and a likely cause in the alert. The heartbeat receiver ships in an upcoming release; see the plans to learn what each tier adds.
Frequently asked questions
- How far back should my backup grace period be?
- Set the grace period above the expected backup duration, plus some margin. A database that normally takes 10 minutes to dump should have at least a 30-minute grace period. A slow or large backup might take 2-3x its normal time before failing — a wide grace avoids false alarms.
- Should I test a restore as part of backup monitoring?
- Yes — a backup file that exists but can't be restored is not a backup. Periodically run pg_restore or mysql in a test environment against the most recent backup file to confirm it is valid. Automate this as a separate cron job with its own heartbeat monitor.