backup: keep deleted/overwritten versions instead of mirroring them away

The nightly job runs 'rclone sync', which permanently deletes or overwrites
objects at the B2 destination. That means an accidental deletion or a
ransomware encryption on /hdd propagates straight to the backup on the next
run, leaving no clean copy.

Add --backup-dir so every superseded version is moved into a dated folder
under _versions/ rather than thrown away, and prune that folder after 30
days so it doesn't grow unbounded.
This commit is contained in:
Rasmus Wejlgaard 2026-06-05 21:21:06 +01:00
parent 7f2cbd4af1
commit 3c5764ff6a

View file

@ -7,6 +7,15 @@ DIRS=(archive backups stash syncthing ftp)
EMAIL="pez@pez.sh" EMAIL="pez@pez.sh"
SUBJECT="HDD Backup Report - $(date '+%Y-%m-%d %H:%M')" SUBJECT="HDD Backup Report - $(date '+%Y-%m-%d %H:%M')"
# Versioning: a plain `rclone sync` permanently deletes/overwrites objects at
# the destination, so a deletion or ransomware encryption on /hdd would
# propagate to the backup on the next run. Instead, move every superseded
# version into a dated folder under $VERSIONS so it can be recovered, then
# prune anything older than $RETENTION_DAYS to cap storage.
STAMP="$(date '+%Y-%m-%d_%H%M%S')"
VERSIONS="$BUCKET/_versions"
RETENTION_DAYS=30
failures=() failures=()
report="" report=""
size_error="" size_error=""
@ -16,7 +25,7 @@ for dir in "${DIRS[@]}"; do
dst="$BUCKET/$dir" dst="$BUCKET/$dir"
echo "Syncing $src -> $dst" echo "Syncing $src -> $dst"
if output=$(rclone sync "$src" "$dst" -v 2>&1); then if output=$(rclone sync "$src" "$dst" --backup-dir "$VERSIONS/$STAMP/$dir" -v 2>&1); then
rc=0 rc=0
else else
rc=$? rc=$?
@ -28,6 +37,14 @@ for dir in "${DIRS[@]}"; do
report+="=== $dir ===\n$output\n\n" report+="=== $dir ===\n$output\n\n"
done done
# Prune versioned copies older than the retention window.
if prune_output=$(rclone delete "$VERSIONS" --min-age "${RETENTION_DAYS}d" -v 2>&1); then
:
else
failures+=("version-prune")
report+="=== Version Prune Error ===\n$prune_output\n\n"
fi
# Get bucket storage usage # Get bucket storage usage
if bucket_usage=$(rclone size "$BUCKET" 2>&1); then if bucket_usage=$(rclone size "$BUCKET" 2>&1); then
: :