How would you build a safe, idempotent rsync-based automation job that syncs production data to a backup target on a schedule, and what specific safeguards keep it from turning a source-side accident, such as an accidental deletion of a subdirectory, into a destroyed backup?
Quick Answer
Use rsync --delete carefully or avoid it entirely in favor of versioned/incremental destinations via --link-dest hardlink snapshots or a destination that supports point-in-time snapshots, always run a --dry-run diff before any destructive sync in automation, and keep multiple generations of backups rather than one continuously-mirrored destination, because a mirrored destination with --delete enabled will faithfully replicate a source-side deletion or corruption straight into your only backup.
Detailed Answer
A naive backup setup that just mirrors production into one destination folder is like having a single photocopier that instantly shreds the old copy the moment you photocopy a new page — if someone tears a page out of the original book by accident, your "backup" copier dutifully shreds that page from its copy too, because it was only ever designed to make the copy match the original exactly, not to remember history.
rsync --delete exists to keep a destination as an exact mirror of the source, which is genuinely useful for things like mirroring a website or a build artifact directory where you want the destination to reflect exactly what's on the source, nothing more and nothing less. But when the same flag is used for what's supposed to be a backup, a copy meant to survive mistakes made on the source, it defeats the entire purpose — a backup that mirrors the source in real time, including deletions, is not a backup against accidental deletion at all, only a backup against total destination loss.
A safer automation design uses rsync --link-dest pointed at the previous backup directory to create space-efficient historical snapshots: each run creates a new destination directory, and for any file that hasn't changed since the last snapshot, rsync creates a hardlink to the existing file in the previous snapshot directory instead of copying it again, so unchanged files cost zero additional disk space while still being independently protected per snapshot generation. Before any run that would use --delete, for cases where mirroring truly is intended, like a CDN origin sync, the automation should first run the exact same command with --dry-run and diff the planned deletions against an expected threshold — if a routine sync is suddenly about to delete 40 percent of files instead of the usual near-zero, that's a signal something upstream is wrong and the run should abort rather than proceed.
In production, the automation itself should be idempotent, safe to re-run after a partial failure without duplicating work or corrupting state, which for rsync is largely free since re-running a sync against a partially-completed destination just resumes where it left off. What needs explicit engineering is bounding blast radius: cap the maximum percentage of files a single run is allowed to delete or overwrite before requiring manual confirmation, retain a fixed number of historical snapshot generations with automatic pruning of the oldest rather than unlimited growth, and emit a structured audit record per run, source path, destination path, files added/changed/deleted, byte counts, exit code, so a security or platform team can reconstruct exactly what happened to any given file across time.
The non-obvious gotcha is that --link-dest snapshots only protect against file content being lost — if an attacker or a bad script targets the backup destination itself rather than the source, all those hardlinked snapshot directories share the same underlying inode data for unchanged files, so a deletion or ransomware-style encryption pass that touches the shared file across snapshots can silently corrupt what looks like multiple independent historical backups simultaneously. True protection against destination-side compromise requires either read-only or immutable storage for older snapshots, filesystem-level immutability flags or object storage with versioning and write-once-read-many policies, or replicating snapshots to a genuinely separate, less-privileged destination, not just relying on directory-level snapshot naming for safety.