What a replica actually promises
rm -rf in the wrong path. These are logical failures. The hardware is fine, the filesystem is consistent, and every write is a perfectly valid write. Replication has no way to tell an encrypting process apart from an editor saving a file, so it copies both.
This is also what tends to confuse site owners when they ask support to restore from the mirror. The mirror exists, it is reachable, and it is up to date. That is precisely the problem.
How rsync and lsyncd carry an encryption run across
--backup and --backup-dir.
The deletion side is worse. Many ransomware families do not overwrite in place. They read the original, write an encrypted copy under a new name (often with an added extension), then unlink the original. From rsync's point of view, the source now has new files and is missing the old ones. If the job runs with --delete, which mirror setups usually do so the copy stays clean, rsync removes the clean originals from the mirror and uploads the encrypted ones. The cron job finishes with exit code 0 and nothing looks unusual in its log.
Before trusting any mirror job, it helps to see what it would do right now. A dry run lists changes and deletions without touching the destination:
BASH
# -n is a dry run: nothing is transferred or deleted
# --itemize-changes shows why each file would be updated
rsync -avn --delete --itemize-changes /var/www/ backup-host:/srv/mirror/www/*deleting are files the mirror would lose on the next real run. On a healthy server this list is short. After an encryption run it would be most of the site.
lsyncd removes even the delay that a cron schedule gives you. It uses inotify, the Linux kernel interface that reports filesystem events on watched directories. When a process closes a file it wrote, the kernel emits IN_CLOSE_WRITE. When a file is renamed, it emits IN_MOVED_FROM and IN_MOVED_TO. When a file is unlinked, it emits IN_DELETE. lsyncd collects these events for a short window, then spawns rsync for the affected paths. An encryption run produces a dense stream of exactly these events, so lsyncd dutifully batches them and pushes the result to the second server. The faster your sync, the faster the damage arrives.
Block-level and database replication copy the damage faster
DROP TABLE, a mass UPDATE without a WHERE clause, or a compromised application rewriting rows all become binlog events. The replica applies them in order, usually within seconds. If the attacker encrypts the data directory files directly instead of going through SQL, the replica may keep its own copy intact, but it is still connected to the same source, so any later damage through SQL lands on it too.
One useful variation is a delayed replica, which applies events only after a fixed lag. That turns replication into a short rewind window for SQL-level mistakes:
SQL
-- MySQL 8.0.23+ syntax; older versions use CHANGE MASTER TO ... MASTER_DELAY
STOP REPLICA;
CHANGE REPLICATION SOURCE TO SOURCE_DELAY = 3600; -- illustrative example: one hour
START REPLICA;Why RPO looks great for disks and terrible for ransomware
Adding the history that a mirror lacks
--link-dest: each run creates a new dated directory, and files unchanged since the previous run are hard-linked instead of copied, so many versions cost little extra space. Running it from the backup server, pulling from production, matters as much as the versioning:
BASH
# Run on the backup server, not on production
set -e
today=$(date +%F)
base=/srv/backups/site
rsync -a --link-dest="base/latest" backup-ro@prod-host:/var/www/ "base/$today/"
ln -sfn "base/today" "$base/latest"