Skip to content

Instantly share code, notes, and snippets.

@oberhamsi
Last active October 27, 2025 18:29
Show Gist options
  • Save oberhamsi/13db66c7376a12d63fc97707d2546921 to your computer and use it in GitHub Desktop.
Save oberhamsi/13db66c7376a12d63fc97707d2546921 to your computer and use it in GitHub Desktop.
#!/bin/bash
# -----------------------------
# Configuration
# -----------------------------
set -ex
SOURCES=(
"/home/"
"/volume1/folder/"
)
REMOTE="secure:"
CURRENT="$REMOTE/current"
OLD="$REMOTE/old"
KEEP_SNAPSHOTS=300 # Keep last 300 snapshots
MAX_DELETES=10 # Warn if more than this many files deleted
# -----------------------------
# Backup
# -----------------------------
TODAY=$(date +%Y-%m-%d)
DAILY_BACKUP="$OLD/$TODAY"
TOTAL_DELETES=0
EXIT_CODE=0
echo "[INFO] Starting backup for multiple sources into $CURRENT"
for SRC in "${SOURCES[@]}"; do
RELPATH=$(realpath --relative-to=/ "$SRC")
TARGET_DIR="$CURRENT/$RELPATH"
BACKUP_DIR="$DAILY_BACKUP/$RELPATH"
echo "[INFO] Syncing $SRC → $TARGET_DIR (backup-dir: $BACKUP_DIR)"
# Run sync and capture output
SYNC_OUTPUT=$(rclone sync "$SRC" "$TARGET_DIR" --backup-dir "$BACKUP_DIR" --exclude "**/@eaDir/**" --skip-links --delete-excluded 2>&1)
SYNC_EXIT=$?
echo "$SYNC_OUTPUT"
# Count deleted files from backup-dir (files moved there are "deleted" from current)
if rclone lsf "$BACKUP_DIR" >/dev/null 2>&1; then
DELETES=$(rclone size "$BACKUP_DIR" --json 2>/dev/null | grep -o '"count":[0-9]*' | cut -d: -f2)
if [[ -n "$DELETES" && "$DELETES" -gt 0 ]]; then
echo "[INFO] $DELETES files deleted/changed in $RELPATH"
TOTAL_DELETES=$((TOTAL_DELETES + DELETES))
fi
fi
if [[ $SYNC_EXIT -ne 0 ]]; then
EXIT_CODE=1
fi
done
# Check for excessive deletes
if [[ $TOTAL_DELETES -gt $MAX_DELETES ]]; then
echo "[WARNING] Excessive deletes detected: $TOTAL_DELETES files (threshold: $MAX_DELETES)"
EXIT_CODE=1
fi
# -----------------------------
# Rotation
# -----------------------------
echo "[INFO] Performing snapshot rotation"
snapshots=$(rclone lsf "$OLD" --dirs-only | sed 's:/$::' | sort)
if [[ -z "$snapshots" ]]; then
echo "[INFO] No snapshots to rotate"
exit $EXIT_CODE
fi
snapshot_count=$(echo "$snapshots" | wc -l)
echo "[INFO] Found $snapshot_count snapshots"
# Keep last KEEP_SNAPSHOTS
keep_snapshots=($(echo "$snapshots" | tail -n "$KEEP_SNAPSHOTS"))
echo "[INFO] Keeping ${#keep_snapshots[@]} snapshots"
# Delete old snapshots
DELETED_COUNT=0
for snap in ${snapshots}; do
if [[ ! " ${keep_snapshots[@]} " =~ " ${snap} " ]]; then
echo "[INFO] Deleting old snapshot: $snap"
if rclone purge "$OLD/$snap"; then
DELETED_COUNT=$((DELETED_COUNT + 1))
else
echo "[ERROR] Failed to delete $snap"
EXIT_CODE=1
fi
fi
done
echo "[INFO] Deleted $DELETED_COUNT old snapshots"
echo "[INFO] Backup and rotation completed"
exit $EXIT_CODE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment