diff --git a/vaultwarden_README.md b/vaultwarden_README.md new file mode 100644 index 0000000..470cc98 --- /dev/null +++ b/vaultwarden_README.md @@ -0,0 +1,220 @@ +# Vaultwarden Backup & Restore Guide + +> [!Note] +> The official backup and restore documentation for Vaultwarden is located on the [Vaultwarden GitHub Wiki](https://github.com/dani-garcia/vaultwarden/wiki/Backing-up-your-vault). The steps below are tailored specifically to our local Docker container setup and automated backup script. + +--- + +# Script Architecture & Workflow + +The custom backup script (`vaultwarden_backup.sh`) automates point-in-time snapshots of Vaultwarden while maintaining database integrity, supporting non-POSIX external backup drives (exFAT/NTFS), and providing real-time notification alerts. + +``` + +-------------------------------------------------------+ + | Pre-flight Checks | + | (Mount point verify -> Data Dir check -> Docker running) + +---------------------------+---------------------------+ + | + v + +-------------------------------------------------------+ + | Step 1: Container DB Snapshot | + | `docker exec vaultwarden /vaultwarden backup` | + | (Generates db_YYYYMMDD_HHMMSS.sqlite3 safely in WAL) | + +---------------------------+---------------------------+ + | + v + +-------------------------------------------------------+ + | Step 2: Sync Snapshot DB | + | `rsync` snapshot -> `CURRENT_BACKUP_DIR/db.sqlite3` | + | (Deletes local temp snapshot file) | + +---------------------------+---------------------------+ + | + v + +-------------------------------------------------------+ + | Step 3: Sync Attachments & Data | + | `rsync` attachments, sends, RSA keys, config | + | (Excludes active live `db.sqlite3*`) | + +---------------------------+---------------------------+ + | + v + +-------------------------------------------------------+ + | Step 4: Update Path Pointer | + | Write `CURRENT_BACKUP_DIR` path -> `latest.txt` | + +---------------------------+---------------------------+ + | + v + +-------------------------------------------------------+ + | Step 5: Retention Cleanup | + | `find` & delete `backup_*` folders > 30 days | + +---------------------------+---------------------------+ + | + v + +-------------------------------------------------------+ + | ntfy Status Alert Sent | + +-------------------------------------------------------+ +``` + +## Detailed Breakdown of Script Operations + +### 1. Pre-flight Infrastructure Checks +Before modifying any data or making temporary local files, the script verifies three critical dependencies: +* **Mount Verification (`mountpoint -q`)**: Ensures `/mnt/5TB-Disk1` is an actively mounted filesystem. This prevents writing gigabytes of backup data to the local host root disk if the external drive gets disconnected. +* **Source Path Verification**: Confirms `/srv/docker/vaultwarden/data` exists. +* **Container State Check**: Verifies that the Vaultwarden Docker container is actively running using `docker ps`. + +If any pre-flight test fails, the script sends an error payload to `ntfy` and exits immediately with status code `1`. + +### 2. Database Snapshot Generation (Hot Backup) +Vaultwarden uses SQLite with Write-Ahead Logging (WAL). Directly copying `db.sqlite3` with standard filesystem tools (`cp`, `rsync`) while Vaultwarden is writing will produce a **corrupted database backup**. + +To prevent corruption, the script invokes Vaultwarden's native backup CLI command inside the container: +```bash +docker exec vaultwarden /vaultwarden backup +``` +* **How it works**: Vaultwarden issues an online SQLite backup API call that safely locks transactions briefly, creates a complete, uncorrupted database copy named `db_YYYYMMDD_HHMMSS.sqlite3` inside the host volume directory (`/srv/docker/vaultwarden/data`), and immediately releases the lock. + +### 3. Isolated Target Snapshot & Normalization +Once generated, the script locates the newest `db_*.sqlite3` snapshot and copies it to the target backup folder as `db.sqlite3`: +* **Naming Standardization**: Renaming `db_YYYYMMDD_HHMMSS.sqlite3` to `db.sqlite3` at the destination creates a turnkey restoration layout matching Vaultwarden's default expectation. +* **Local Cleanup**: Immediately after copying, `rm -f "$LATEST_DB_BACKUP"` removes the temporary snapshot from host storage (`/srv/docker/vaultwarden/data/`) to prevent local disk exhaustion. + +### 4. Asset Synchronization (`rsync`) +Attachments, user sends, encryption keys (`rsa_key*`), and server configurations (`config.json`) are synced using `rsync`: +```bash +rsync -av --no-owner --no-group --exclude='db.sqlite3*' /srv/docker/vaultwarden/data/ "$CURRENT_BACKUP_DIR/" +``` +* **Flag Explanation**: + * `-a` (Archive mode): Preserves modification times, recursive directory structure, and file permissions. + * `--no-owner --no-group`: Prevents `rsync` from throwing permission warnings when backing up to filesystems (like exFAT or NTFS) that do not support POSIX user/group ownership mapping. + * `--exclude='db.sqlite3*'`: Prevents active WAL journal files (`db.sqlite3-wal`, `db.sqlite3-shm`) from being copied alongside the static `db.sqlite3` snapshot. + +### 5. Filesystem-Agnostic "Latest" Pointer +On standard Linux filesystems (ext4/btrfs), scripts usually create a symbolic link (`ln -s`) to mark the latest backup folder. However, drives formatted as **exFAT, FAT32, or NTFS** reject symlinks with `Operation not permitted`. + +To keep the script 100% compatible with any storage medium: +* The script writes the absolute path of the newest backup folder into a plain text file: `/mnt/5TB-Disk1/backup/services/vaultwarden/latest.txt`. +* Restores or external monitoring scripts can simply read `cat latest.txt` to discover the active backup location. + +### 6. Retention Engine +Automatic backup rotation is performed using `find`: +```bash +find "$VAULTWARDEN_DESTINATION" -maxdepth 1 -type d -name "backup_*" -mtime +30 -exec rm -rf {} + +``` +* `-maxdepth 1`: Restricts search depth to the top-level Vaultwarden backup directory to avoid recursing into subdirectories. +* `-name "backup_*"`: Filters strictly for directories generated by this script. +* `-mtime +30`: Identifies folders whose file modification time is strictly older than 30 days (720 hours). +* `-exec rm -rf {} +`: Deletes matching historical backup directories in a single batch operation. + +### 7. Event Notifications via ntfy +All execution pathways (successes, container missing, unmounted drives, `rsync` failures) trigger an HTTP `POST` request to the defined `ntfy` server via `curl`, delivering real-time status updates directly to connected clients. + +--- + +# Automated Backup Execution + +## Cron Configuration + +The backup script is executed automatically via root's crontab. + +To view or edit the automated schedule: +```bash +sudo crontab -e +``` + +**Recommended Schedule (Every night at 2:30 AM):** +```cron +30 2 * * * /bin/bash /srv/docker/vaultwarden/vaultwarden_backup.sh >/dev/null 2>&1 +``` + +## Manual Script Execution + +To manually trigger a complete backup at any time: +```bash +sudo /srv/docker/vaultwarden/vaultwarden_backup.sh +``` + +--- + +# Manual Backup Procedure + +If you need to perform a backup manually without using the script: + +### 1. Generate Container Database Snapshot +```bash +sudo docker exec vaultwarden /vaultwarden backup +``` + +### 2. Copy Snapshot and Assets +```bash +# Define target directory +TARGET_DIR="/mnt/5TB-Disk1/backup/services/vaultwarden/manual_$(date +%Y-%m-%d_%H%M%S)" +mkdir -p "$TARGET_DIR" + +# Locate and copy the generated DB snapshot +LATEST_DB=$(ls -t /srv/docker/vaultwarden/data/db_*.sqlite3 | head -n 1) +sudo rsync -av --no-owner --no-group "$LATEST_DB" "$TARGET_DIR/db.sqlite3" + +# Remove the temporary snapshot from source +sudo rm -f "$LATEST_DB" + +# Copy remaining assets (RSA keys, attachments, sends, config) +sudo rsync -av --no-owner --no-group --exclude='db.sqlite3*' /srv/docker/vaultwarden/data/ "$TARGET_DIR/" +``` + +--- + +# Restore Vaultwarden Procedure + +> [!Caution] +> Always stop the Vaultwarden container before restoring files. Overwriting files on an active container can cause database locks or file corruption. + +### Step 1: Identify Backup to Restore +Identify the backup directory path from `latest.txt` or list all available timestamped backups: + +```bash +# View path to the most recent successful backup +cat /mnt/5TB-Disk1/backup/services/vaultwarden/latest.txt + +# Or list all historical backup directories +ls -l /mnt/5TB-Disk1/backup/services/vaultwarden/ +``` + +### Step 2: Stop Vaultwarden Container +```bash +sudo docker stop vaultwarden +# OR if using Docker Compose: +# cd /srv/docker/vaultwarden && sudo docker compose down +``` + +### Step 3: Restore Data Files +Rsync the database snapshot, attachments, sends, and RSA keys back to the host data directory. + +> [!Note] +> Replace `/mnt/5TB-Disk1/backup/services/vaultwarden/backup_YYYY-MM-DD_HHMMSS` below with the target backup directory you wish to restore. + +```bash +sudo rsync -av --delete /mnt/5TB-Disk1/backup/services/vaultwarden/backup_2026-10-03_101124/ /srv/docker/vaultwarden/data/ +``` + +### Step 4: Verify Host File Permissions +Ensure the restored files match the container user's expected ownership (UID/GID `1000:1000` default): + +```bash +sudo chown -R 1000:1000 /srv/docker/vaultwarden/data +``` + +### Step 5: Start Vaultwarden Container & Verify Logs +```bash +sudo docker start vaultwarden +# OR if using Docker Compose: +# cd /srv/docker/vaultwarden && sudo docker compose up -d + +# Verify startup logs +sudo docker logs -f vaultwarden +``` + +--- + +## Authors and Acknowledgment +* **Vaultwarden Wiki**: https://github.com/dani-garcia/vaultwarden/wiki +* **Vaultwarden Repository**: https://github.com/dani-garcia/vaultwarden \ No newline at end of file