Blog 2026-07-24
Backup Failed Silently: Detect Missing Backups Before You Need Them
How silent backup failures happen and how heartbeat monitoring detects missed backup jobs before disaster recovery.
Quick answer: A backup “succeeds” in cron terms if the process exits 0 — even when the dump is empty, the upload never happened, or the crontab never fired. Ping Seiri only after a verified artifact exists (and preferably after upload). If the check-in stops, you are alerted before restore day.
The worst time to discover a backup failed silently is during recovery. This is one of the clearest fits for a dead man’s switch: absence of a success signal is the incident.
Seiri is heartbeat / push monitoring — Did the backup job check in? — not disk uptime and not APM.
How backups fail without alarms
Walk this list against your last incident (or your current script):
- Upload failed, exit 0 —
aws s3 cpfailed but the shell continued withoutset -e. - Credentials expired — dump skipped or wrote zero bytes; cron still “ran.”
- Disk full mid-dump — partial
.sql.gzleft on disk; “file exists” checks pass. - Crontab removed after host migration — nothing runs; cron job not running.
- Kubernetes CronJob
suspend: trueleft on after a maintenance window. - Mutex /
withoutOverlapping/flockstuck — subsequent runs skip quietly. - Wrong database / wrong cluster — script backs up empty staging credentials in prod env mixups.
- Encryption or checksum step skipped after a refactor — artifact unusable, job green.
Presence of a file on disk is a weak signal. An explicit success check-in after verification is stronger. See also cron job failed silently.
Diagnosis checklist
1. Did the scheduler fire?
crontab -l
grep CRON /var/log/syslog | grep -i backup | tail -20
# K8s:
kubectl get cronjobs,jobs -A | grep -i backupNo schedule fire → fix cron/CronJob first; monitoring below still required afterward.
2. Is there an artifact, and is it non-empty?
ls -lh /var/backups/
find /var/backups -type f -mtime -1 -ls
# empty file check
[[ -s /var/backups/db-latest.sql.gz ]] && echo ok || echo EMPTY3. Did the remote copy land?
aws s3 ls s3://my-backups/ --recursive | tail
# or rclone, gsutil, az storage — confirm size and timestampCompare local size to remote size. Mismatch → silent partial failure.
4. Can you restore a recent artifact to a scratch instance?
A green job that produces unrestorable blobs is still a silent failure. Schedule restore drills; monitor those jobs too (scheduled job monitoring).
5. Exit code and shell options
bash -x /usr/local/bin/backup-database.sh; echo exit:$?If critical steps lack set -euo pipefail, fix that before anything else.
Monitoring fix: heartbeat the verified success path
Hardened backup script
#!/usr/bin/env bash
set -euo pipefail
dump="/var/backups/db-$(date +%F).sql.gz"
umask 077
pg_dump "$DATABASE_URL" | gzip > "$dump"
[[ -s "$dump" ]] || { echo "empty dump" >&2; exit 1; }
# optional: minimum size guard (tune to your DB)
min_bytes=1048576
size=$(wc -c < "$dump")
(( size >= min_bytes )) || { echo "dump too small: $size" >&2; exit 1; }
aws s3 cp "$dump" "s3://my-backups/$(basename "$dump")"
# optional: head object and compare content-length
# caller pings Seiri only if we reach hereCrontab with Seiri
0 2 * * * /usr/local/bin/backup-database.sh && \
curl -fsS -m 10 --retry 5 \
https://ping.seiri.app/webhook/YOUR_ORG:YOUR_REF/success \
|| curl -fsS -m 10 \
https://ping.seiri.app/webhook/YOUR_ORG:YOUR_REF/failOr success-only (missed run = silence → alert):
0 2 * * * /usr/local/bin/backup-database.sh && \
curl -fsS -m 10 --retry 5 \
https://ping.seiri.app/webhook/YOUR_ORG:YOUR_REFCreate the monitor in cloud.seiri.app (free forever). Schedule 0 2 * * * with grace covering dump + upload + variance (often 30–90 minutes depending on DB size).
No HTTPS egress
0 2 * * * /usr/local/bin/backup-database.sh && \
echo "backup ok $(date -u +%FT%TZ)" | \
mail -s "db-backup" YOUR_REF@ping.seiri.appSee email heartbeat monitoring.
Application schedulers
Same placement rule — ping after verify:
- Laravel:
Http::get('https://ping.seiri.app/webhook/YOUR_ORG:YOUR_REF')at end ofapp:backup-database, orpingOnSuccess(...). - Celery / Sidekiq: ping after upload confirms.
- Kubernetes CronJob: curl in the Job container after
/backup.sh, or seiri-kube-agent (kubernetes-agent).
Verify restores on a schedule
Heartbeat proves “job reported success.” It does not prove “restore works.” Add a periodic restore-to-scratch job with its own Seiri monitor. If the drill is skipped, you want that alert as much as a missed nightly dump.
Practices that prevent silent backup failure
set -euo pipefail(or fail-hard equivalents).- Reject empty / undersized dumps before upload.
- Confirm remote object size when the API allows.
timeout+flockso hung dumps cannot block forever without detection (heartbeat grace still catches silence).- One Seiri monitor per backup lane (primary DB, object storage, configs) — do not share one ping across unrelated jobs.
- Config-manage the crontab / CronJob; treat missing schedules as deploy bugs.
- Alert to PagerDuty/Slack — not only email to a shared inbox humans ignore.
Related
Transform Your Monitoring
Get started with Seiri
Heartbeat monitoring for cron jobs, workers, and Kubernetes — know when scheduled work goes silent.
Start free- Free forever
- No credit card required
- Cancel anytime
Would you know if this job stopped running?
Seiri watches your cron jobs, workers, and Kubernetes CronJobs, and alerts you the moment one goes silent. Free forever, no credit card.
Start free