Files
media-server/vaultwarden_README.md

10 KiB

Vaultwarden Backup & Restore Guide

Note

The official backup and restore documentation for Vaultwarden is located on the Vaultwarden GitHub Wiki. 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:

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:

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:

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:

sudo crontab -e

Recommended Schedule (Every night at 2:30 AM):

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:

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

sudo docker exec vaultwarden /vaultwarden backup

2. Copy Snapshot and Assets

# 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:

# 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

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.

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):

sudo chown -R 1000:1000 /srv/docker/vaultwarden/data

Step 5: Start Vaultwarden Container & Verify Logs

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