Files
scripts/vaultwarden-backup-helpers/README.md
bdm d27e93d220 Track vaultwarden backup and restore scripts
Daily SQLite backup to the NetBackup SMB share plus restore/import/export
helpers.

SMB credentials moved out of the scripts into ~/.smbcredentials (mode 600)
and are passed to smbclient via -A instead of -U "user%pass". This keeps
the password out of version control and out of the process list, where
-U exposes it to any local user through ps.
2026-09-28 06:24:25 -07:00

5.3 KiB

Vaultwarden Backup & Restore System

This directory contains scripts for backing up, restoring, and converting Vaultwarden data.

Overview

Primary Backup: SQLite Database (Automated Daily)

The main backup runs daily at 3:00 AM via cron (/home/bdm/scripts/vaultwarden-backup.sh). It:

  1. Creates a consistent SQLite backup using sqlite3 .backup
  2. Uploads to SMB share (//192.168.123.2/NetBackup/Vault/)
  3. Cleans up backups older than 60 days

This is the complete backup for disaster recovery. It contains ALL users, their encrypted vaults, organizations, collections, sends, etc.

Restore Options

Use restore-sqlite.sh to restore the entire Vaultwarden instance:

./restore-sqlite.sh vaultwarden-2026-09-19_09-03-45.db

This replaces the database file and restarts Vaultwarden. All users log in with their existing master passwords.

Option 2: Individual User Vault Export/Import (JSON)

Use export-to-json.sh and import-from-json.sh for individual user vaults:

# Export (requires master password)
./export-to-json.sh user@example.com my-vault.json

# Import (requires master password)
./import-from-json.sh user@example.com my-vault.json

Note: JSON export requires the master password because the SQLite database stores all data ENCRYPTED. The master password is needed to decrypt the vault for JSON export.

Scripts

Script Purpose Requires Master Password
vaultwarden-backup.sh Daily automated SQLite backup (run by cron) No
restore-sqlite.sh Restore entire instance from SQLite backup No
export-to-json.sh Export single user vault to JSON Yes
import-from-json.sh Import JSON vault to user account Yes
mount-smb.sh Mount SMB share for cleanup (internal) No
umount-smb.sh Unmount SMB share (internal) No
cleanup-old-backups.sh Delete backups older than 60 days (internal) No

Backup Location

  • SMB Share: //192.168.123.2/NetBackup/Vault/
  • Local Temp: /tmp/vaultwarden-backup/ (cleaned up after upload)
  • Log File: /var/log/vaultwarden-backup.log

Cron Schedule

Daily at 3:00 AM (see crontab -l):

0 3 * * * /home/bdm/scripts/vaultwarden-backup.sh >> /var/log/vaultwarden-backup.log 2>&1

Manual Backup

To run a manual backup:

/home/bdm/scripts/vaultwarden-backup.sh

Sudo Configuration

The backup script uses sudo for specific commands. Sudoers rules are in /etc/sudoers.d/vaultwarden-backup:

  • sqlite3 backup command
  • chown for temp files
  • SMB mount/umount/cleanup wrapper scripts

Disaster Recovery Procedure

  1. New server setup:

    • Install Docker, Docker Compose
    • Deploy Vaultwarden container with same volume name (caddy_vaultwarden_data)
    • Start container once to create empty database
  2. Restore database:

    # Download backup from SMB (credentials read from the file below)
    smbclient //192.168.123.2/NetBackup -A "$HOME/.smbcredentials" -c "cd Vault; get vaultwarden-YYYY-MM-DD_HH-MM-SS.db"
    
    # Restore
    ./restore-sqlite.sh vaultwarden-YYYY-MM-DD_HH-MM-SS.db
    
  3. Verify: Users can log in with their existing credentials

JSON Import/Export Notes

  • JSON files contain DECRYPTED vault data - store securely!
  • Use BW_MASTER_PASSWORD env var for automation (less secure)
  • Interactive prompt is more secure
  • JSON format is Bitwarden-compatible (select "Bitwarden (json)" on import)
  • Import does not overwrite existing items - it adds/updates

Troubleshooting

Backup fails with "sqlite3 backup failed"

  • Check if container is running: docker ps | grep vaultwarden
  • Check volume path: ls -la /var/lib/docker/volumes/caddy_vaultwarden_data/_data/
  • Run manually to see errors: /home/bdm/scripts/vaultwarden-backup.sh

SMB upload fails

  • Check network connectivity: ping 192.168.123.2
  • Check the SMB credentials file is present and readable: ls -l ~/.smbcredentials
  • Test manually: smbclient //192.168.123.2/NetBackup -A "$HOME/.smbcredentials" -c "ls"

Cron doesn't run

  • Check cron logs: journalctl -u cron -f
  • Verify crontab: crontab -l
  • Check log file: cat /var/log/vaultwarden-backup.log

SMB Credentials

The scripts do not contain the SMB password. It lives in an external file so that it stays out of version control and out of the process list:

  • Location: ~/.smbcredentials (override with the SMB_CRED_FILE env var)
  • Permissions: 600, owned by the user running the backup (bdm)

Create it once, with mode 600:

umask 077
printf 'username = %s\npassword = %s\n' 'bdm' 'YOUR_PASSWORD' > ~/.smbcredentials

The scripts pass it to smbclient with -A (authentication file), which is preferable to -U "user%pass" because a password passed via -U is visible to any local user through ps.

Note that /root/.smbcredentials is a separate file used only by the sudo mount -t cifs calls, which run as root. The two are independent.

Security Notes

  • SMB credentials are kept in ~/.smbcredentials (mode 600), not in the scripts
  • JSON exports contain plaintext passwords - encrypt them if storing long-term
  • SQLite backups contain encrypted data - safe to store but treat as sensitive
  • Master passwords are never stored - only used interactively or via env var