A simple setup for backing up Vaultwarden (formerly bitwarden_rs) data/config to cloud storage
Note: Vaultwarden was formerly known as bitwarden_rs.
This repo contains my automated setup for SQLite-based Vaultwarden backups. It's designed solely to meet my own backup requirements (i.e., not to be general purpose):
Generate a single archive with a complete backup of all Vaultwarden data and config on a configurable schedule.
Retain backup archives on the local Vaultwarden host for a configurable number of days.
Upload encrypted copies of the backup archives to one or more cloud storage services using rclone. The retention policy is configured/managed at the storage service level.
Return success when all backup archives are successfully uploaded, or failure if any uploads fail. This allows cron monitoring services like Healthchecks.io, Cronitor, or Dead Man’s Snitch to provide notification of backup failures.
For the most part, I'm not looking for contributions or feature requests, as this repo is only intended to implement my own backup requirements. I may be willing to make some minor generalizations to make it easier for people to use the repo without modification, but aside from that, feel free to fork and modify this setup to fit your own needs.
Note: This single-archive backup scheme isn't space-efficient if your vault includes large file attachments, as they will be re-uploaded with each backup. If this is an issue, you might consider modifying the script to use restic instead.
A standard Unix-like (preferably Linux) host running Vaultwarden. I don't know much about Synology or other such environments.
A cron daemon. This is used to run backup actions on a scheduled basis.
An sqlite3
binary (https://sqlite.org/cli.html). This is used to back up
the SQLite database. This can be installed via the sqlite3
package on
Debian/Ubuntu or the sqlite
package on RHEL/CentOS/Fedora.
An rclone
binary (https://rclone.org/). This is used to copy the backup
archives to cloud storage. This can be installed via the rclone
package
on Debian/Ubuntu and RHEL/CentOS/Fedora (EPEL
required for RHEL/CentOS), but as rclone changes more rapidly, it's probably
best to just use the latest binary from https://rclone.org/downloads/.
An account at one or more cloud storage services supported by rclone. If you don't have one yet, here are a few cloud storage services that offer a free tier:
Optionally, a gpg
(GnuPG 2.x) binary (https://gnupg.org/). This can be
installed via the gnupg
package on Debian/Ubuntu or the gnupg2
package
on RHEL/CentOS/Fedora.
Optionally, an age
binary (https://github.com/FiloSottile/age). This option
requires a custom version
of the tool that supports reading the passphrase from an environment variable.
Start by cloning this repo to the directory containing your Vaultwarden
data directory, under the name backup
. In my setup, it looks like this:
$HOME/vaultwarden # Top-level Vaultwarden directory
├── backup # This backup repo
└── data # Vaultwarden data directory
Copy the backup.conf.template
file to backup.conf
.
If you want encrypted backup archives using gpg
, set the
GPG_PASSPHRASE
variable accordingly. If you want to encrypt using
age
instead, set the AGE_PASSPHRASE
variable. If both variables are
set, only gpg
encryption will be performed. If you don't want
encryption at all, comment out both variables or set them to be blank.
This passphrase is used to encrypt the backup archives, which may
contain somewhat sensitive data in plaintext in config.json
(the
password entries themselves are already encrypted by Bitwarden). It
should be something easy enough for you to remember, but complex enough
to deter, for example, any unscrupulous cloud storage personnel who
might be snooping around. As this passphrase is stored on disk in
plaintext, it definitely should not be your Bitwarden master passphrase
or anything similar.
rclone crypt is another option for encrypted
archives. If you prefer to use this method, just set GPG_PASSPHRASE
to
be blank, configure rclone crypt appropriately, and use the crypt remote
in RCLONE_DESTS
.
Change RCLONE_DESTS
to your list of rclone destinations. You'll have
to configure rclone appropriately first.
Note that backup.conf
is simply sourced into the backup.sh
script, so
you can add arbitrary environment variables into backup.conf
as needed.
This can be useful for configuring any tools called from backup.sh
,
such as rclone
.
Modify the backup/crontab.template
file as needed. This crontab actually
calls cron.sh
to run the backup, rather than calling backup.sh
directly.
Currently, cron.sh
captures the output of the current run of backup.sh
to a backup.log
file. It also saves a copy of this log file, named
according to whether the backup run was a success or failure. You can add
other custom logic to cron.sh
if needed, such as signaling failure to a
cron monitoring service.
If $HOME/vaultwarden
isn't your top-level Vaultwarden directory, adjust
the paths in this file accordingly.
Review the backup schedule. I generate backup archives hourly, but you might prefer to do this less frequently to save space.
Review the local backup archive retention policy. I delete archives
older than 14 days (-mtime +14
). Adjust this if needed.
Review the log file retention policy. I delete log files older than
14 days (-mtime +14
). Adjust this if needed.
Review the SQLite VACUUM schedule, or remove the job if you don't want vacuuming. Vacuuming compacts the database file so that operations are faster and backups are smaller.
Install the crontab under a user (typically your normal login) that can
read your Vaultwarden data. In many cases, running crontab -e
and pasting
the contents of the filled-in crontab template file should work. Note that
if your cron user doesn't have write permissions to the database, then you
must ensure it has write permissions to the Vaultwarden data directory,
as SQLite may need to create a -wal
file for the database if it doesn't
already exist. If it's unable to do this, the backup will fail with an
attempt to write a readonly database
error. (For more details, see
https://sqlite.org/wal.html#read_only_databases.)
If you use GnuPG 2.1 or later, see the note about --pinentry-mode loopback
in backup.sh
.
If everything is working properly, you should see the following:
backup/archives
.backup/backup.log
.backup/logs
.For example:
$HOME/vaultwarden/backup
├── archives
│ ├── vaultwarden-20210101-0000.tar.xz
│ ├── vaultwarden-20210101-0000.tar.xz.gpg
│ ├── vaultwarden-20210101-0100.tar.xz
│ ├── vaultwarden-20210101-0100.tar.xz.gpg
│ └── ...
├── backup.conf
├── backup.conf.template
├── backup.log
├── backup.sh
├── cron.sh
├── crontab.template
├── logs
│ ├── backup-success-20210101-0000.log
│ ├── backup-success-20210101-0100.log
│ ├── backup-failure-20210101-0200.log
│ └── ...
├── LICENSE
└── README.md