220 lines
10 KiB
Markdown
220 lines
10 KiB
Markdown
# 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 |