7 September 2026
Silent backup failures: how spatie/laravel-backup breaks without telling you
Your backup job went green last night. The night before that, and every night for the past six months. So your data is safe — right up until the day you need a restore and discover the last usable backup is from March.
This happens more often than anyone admits, and spatie/laravel-backup — excellent as it is —
can't protect you from the most dangerous failure modes on its own. Here's why.
The failure you're watching for isn't the one that gets you
Spatie's package has good error handling: if a backup runs and fails, it fires a
BackupHasFailed event and sends a notification. Most teams wire that up to mail or Slack and
consider the problem solved.
But look at what that covers: failures during a run. The failures that actually cost people their data are the ones where no run happens at all — and if no run happens, there's no event, no notification, and no error in any log you're watching.
The five silent failure modes
1. The scheduler stops. A deploy rewrites your crontab, a server migration misses the cron
entry, schedule:run starts erroring before it reaches your backup task. Nothing runs, so
nothing fails, so nothing alerts. Absence of failure is not success — but every dashboard you
have reads it that way.
2. Notifications share the app's fate. Backup failure notifications are sent by the same Laravel app that's failing. If the queue worker is down, the notification sits in a dead queue. If your mail credentials expired, the alert about the broken backup bounces silently. The messenger dies with the message.
3. The dump is truncated. mysqldump or pg_dump hits an error mid-stream — a lock
timeout, a dropped connection, disk pressure — and you get a file that exists, has a
plausible size, lands in the bucket, and dies at line 40,000 on restore. The backup job can
report success because a file was produced.
4. Credentials rot. The S3 key gets rotated, the bucket policy changes, the disk quota fills. Depending on where it breaks, you either get a failure notification (good) or a backup that quietly stops including what you think it includes.
5. The backup works and the restore doesn't. Encryption key not in the vault, a schema that's drifted from what the dump expects, an extension missing on the target. The file is perfect and useless. No monitoring catches this — only actually restoring does.
What monitoring has to do about it
The common thread: you cannot detect these from inside the thing that's failing. Monitoring backups properly means three separate layers:
- Record every run — success and failure, with sizes and durations, somewhere you'll actually look. A run history makes a truncated dump visible as a size anomaly before you need the file.
- Detect the missing run — a dead-man switch. The question is never "did a backup fail?" It's "did a backup happen in the window one should have?" Nothing inside the failed schedule can answer that.
- Alert on a channel that doesn't depend on the app — if the notification goes through the same queue, mailer, and credentials as the app being monitored, it fails with them.
And when the stakes justify it, there's a fourth layer: restore the backup somewhere real, run the app against it, and prove the thing actually works. A backup you've never restored is a hypothesis.
The short version
- A green backup job proves a run finished, not that a backup happened last night, and not that the file restores.
- The dangerous failures are silent: dead schedulers, dead queues, truncated dumps.
- Monitor from outside the app: run history, missed-run detection, independent alerting.
- Restore-test anything you can't afford to lose.
We build this. Backup Monitor (free) records every spatie/laravel-backup run in a Filament panel. Backup Monitor Pro adds the missed-backup detection and multi-channel alerting this post is about — the dead-man switch for layer two. If you run backups for multiple sites, the Collector puts every site's backup health in one dashboard.