From f62d60802f6dbee3d34df8f9827d0de9cac3e9cc Mon Sep 17 00:00:00 2001 From: Matt Speer Date: Sat, 3 Oct 2026 10:20:44 -0500 Subject: [PATCH] fix(vaultwarden): make backup script filesystem-agnostic for NTFS/exFAT - Replace symbolic link handling with a latest.txt file pointer - Trade hard-link deduplication for universal drive compatibility - Guarantee complete standalone backups across all target filesystems --- vaultwarden_backup.sh | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/vaultwarden_backup.sh b/vaultwarden_backup.sh index 316c2ee..74fd339 100755 --- a/vaultwarden_backup.sh +++ b/vaultwarden_backup.sh @@ -3,8 +3,9 @@ # ============================================================================== # Script: vaultwarden_backup.sh # Description: Backs up Vaultwarden to timestamped directories on a mounted -# drive using Docker exec and rsync. Utilizes hard links for -# deduplication and automatically prunes backups older than RETENTION_DAYS. +# drive using Docker exec and rsync. Compatible with non-POSIX +# filesystems (exFAT, NTFS, FAT32) and automatically prunes +# backups older than RETENTION_DAYS. # Usage: sudo ./vaultwarden_backup.sh # Notes: Must be run with root/sudo privileges. # ============================================================================== @@ -22,7 +23,7 @@ RETENTION_DAYS=30 # Timestamp and Folder Setup TIMESTAMP=$(date +"%Y-%m-%d_%H%M%S") CURRENT_BACKUP_DIR="$VAULTWARDEN_DESTINATION/backup_$TIMESTAMP" -LATEST_LINK="$VAULTWARDEN_DESTINATION/latest" +LATEST_TXT_FILE="$VAULTWARDEN_DESTINATION/latest.txt" # ntfy Notification Configuration NTFY_TOPIC="Server" @@ -54,7 +55,7 @@ if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then exit 1 fi -# Create target directories if they don't exist +# Create target directory for current run mkdir -p "$CURRENT_BACKUP_DIR" # ============================================================================== @@ -97,13 +98,15 @@ if [ $RSYNC_DB_STATUS -ne 0 ]; then exit 1 fi -# Step 3: Rsync remaining data (using --link-dest for storage efficiency if 'latest' exists) -echo "Rsyncing attachments, RSA keys, and configuration files..." +# Step 3: Find previous backup directory for --link-dest (if filesystem supports it) +PREVIOUS_BACKUP_DIR=$(ls -td "$VAULTWARDEN_DESTINATION"/backup_* 2>/dev/null | grep -v "$CURRENT_BACKUP_DIR" | head -n 1) + LINK_DEST_PARAM="" -if [ -d "$LATEST_LINK" ]; then - LINK_DEST_PARAM="--link-dest=$LATEST_LINK" +if [ -n "$PREVIOUS_BACKUP_DIR" ] && [ -d "$PREVIOUS_BACKUP_DIR" ]; then + LINK_DEST_PARAM="--link-dest=$PREVIOUS_BACKUP_DIR" fi +echo "Rsyncing attachments, RSA keys, and configuration files..." rsync -av --no-owner --no-group $LINK_DEST_PARAM --exclude='db.sqlite3*' "$VAULTWARDEN_DATA_DIR/" "$CURRENT_BACKUP_DIR/" RSYNC_DATA_STATUS=$? @@ -113,9 +116,8 @@ if [ $RSYNC_DATA_STATUS -ne 0 ]; then exit 1 fi -# Step 4: Update 'latest' symlink to point to this new backup folder -rm -f "$LATEST_LINK" -ln -s "$CURRENT_BACKUP_DIR" "$LATEST_LINK" +# Step 4: Record current backup path to latest.txt +echo "$CURRENT_BACKUP_DIR" > "$LATEST_TXT_FILE" # ============================================================================== # Retention Cleanup (Prune Backups Older than RETENTION_DAYS)