SSD Nodes Learn 🎉 VPS ab $4.99/Monat
Anleitungen Matt ConnorVon Matt Connor

Vaultwarden-Backup und Restore auf einem VPS

Sichern Sie Vaultwarden mit sqlite3 .backup, Anhänge, config.json und rsa_key-Dateien. Prüfen Sie die Wiederherstellung, bevor Sie sie im Notfall brauchen.

Was ein Vaultwarden-Backup enthalten muss

Ein Vaultwarden-Backup ist eine Kopie des gesamten Datenverzeichnisses. Die darin enthaltene Datenbank muss auf die richtige Weise kopiert werden. Führen Sie sqlite3 db.sqlite3 ".backup out.sqlite3" anstelle von cp aus, weil eine einfache Kopie einer Datenbank, in die gerade geschrieben wird, eine Datei erzeugen kann, die sich nicht öffnen lässt. Sichern Sie anschließend auch die Dateien, die daneben liegen. Dieser Teil wird häufig vergessen.

Bei einer Docker-Installation ist das Datenverzeichnis das Verzeichnis, das Sie an /data eingebunden haben. Dabei handelt es sich entweder um einen Pfad auf dem Host oder um ein benanntes Volume. Der Unterschied zwischen Bind-Mounts und benannten Volumes bestimmt, wo Ihr Tresor tatsächlich auf der Festplatte liegt. Das Verzeichnis enthält Folgendes:

  • db.sqlite3: jedes Konto, jedes Tresorelement, jeden Ordner und jede Organisation. Wenn diese Datei verloren geht, ist der Tresor verloren.
  • db.sqlite3-wal und db.sqlite3-shm: das Write-Ahead-Log (WAL) und seinen gemeinsamen Speicherindex. Kürzlich ausgeführte Schreibvorgänge werden hier gespeichert, bis SQLite sie in die Hauptdatei übernimmt.
  • attachments/: die Dateien, die Benutzer an Tresorelemente angehängt haben, verschlüsselt und in jeweils einem Verzeichnis pro Element.
  • sends/: die Dateien hinter Bitwarden-Send-Links.
  • config.json: jede Einstellung, die Sie auf der Administrationsseite gespeichert haben.
  • rsa_key.pem sowie bei älteren Installationen rsa_key.der und rsa_key.pub.der: der Schlüssel, mit dem Anmeldetoken signiert werden.
  • icon_cache/: heruntergeladene Website-Symbole. Dieses eine Verzeichnis können Sie auslassen, weil Vaultwarden die Symbole bei Bedarf erneut abruft.

Ist meine Vaultwarden-Datenbank sicher? Was die Datei tatsächlich enthält

Zwei Befehle beantworten diese Frage. Sie können beide sofort ausführen.

sudo apt update && sudo apt install -y sqlite3
sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 "select email from users;"
sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 "select name from ciphers limit 1;"

Der erste gibt die E-Mail-Adressen Ihrer Benutzer im Klartext aus. Der zweite gibt einen Elementnamen aus. Er sieht so aus:

2.k9Qw1nQ0y7Yy2Xw==|E1r0J3l5s7d9f1g3h5j7k9==|Lm4nOp6qRs8tUv0wXy2zAb4cDe6fGh8i=

Elementnamen, Benutzernamen, Passwörter und Notizen werden vom Client verschlüsselt, bevor sie übertragen werden. Der Server speichert daher Chiffretext, den er nicht lesen kann. Das Präfix 2. bezeichnet den Verschlüsselungstyp von Bitwarden. Darauf folgen ein Initialisierungsvektor (IV), der Chiffretext und ein MAC (Message Authentication Code), jeweils in base64 und durch | getrennt. Der Schlüssel zur Entschlüsselung wird aus dem Master-Passwort des Kontos abgeleitet. Dieses erreicht den Server nie in einer nutzbaren Form. Dieser Teil ist identisch, unabhängig davon, ob Sie Vaultwarden oder den offiziellen Server betreiben, wie der Vergleich von Vaultwarden und selbst gehostetem Bitwarden ausführlich erläutert.

Der restliche Teil der Datenbank ist nicht verschlüsselt. E-Mail-Adressen, Kontonamen, Passworthinweise und Wiederherstellungscodes für die Zwei-Faktor-Authentifizierung werden im Klartext gespeichert. Daneben enthält die Datenbank Metadaten wie Erstellungszeitpunkte und die Information, welcher Organisation ein Element gehört. Daher ist die Sicherungsdatei selbst ein Geheimnis. Jeder, der sie besitzt, erfährt, wer Ihre Benutzer sind, und kann die verschlüsselten Datenblöcke offline mit der Geschwindigkeit seiner Hardware angreifen. Diese Tatsache bestimmt die weiter unten beschriebenen Regeln für die Speicherung: Die Kopie wird verschlüsselt, bevor sie den Server verlässt.

Warum das Kopieren von db.sqlite3 während des laufenden Vaultwarden kein Backup ist

Vaultwarden verwendet SQLite standardmäßig im WAL-Modus (ENABLE_DB_WAL=true). Ein Schreibvorgang landet zuerst in db.sqlite3-wal. Erst ein Checkpoint übernimmt ihn in db.sqlite3. Wenn Sie db.sqlite3 allein kopieren, erhalten Sie den Datenbankstand des letzten Checkpoints. Ein vor zehn Minuten gespeichertes Passwort kann daher in Ihrem Archiv fehlen, ohne dass Sie gewarnt werden.

Auch das Kopieren aller drei Dateien mit cp ist keine Lösung. Die Kopien werden zu leicht unterschiedlichen Zeitpunkten erstellt. Dadurch kann das gesicherte WAL Seitenversionen beschreiben, die nicht mehr zum gesicherten Hauptfile passen. SQLite stellt dann eine Datei aus der anderen wieder her. Das Ergebnis ist falsch. Sie bemerken es erst viel später:

Error: database disk image is malformed

.backup vermeidet dieses Problem, weil es die SQLite Online Backup API verwendet. SQLite dokumentiert sie als Verfahren zum Kopieren einer Datenbank, die möglicherweise aktiv verwendet wird. Das Tool liest die Seiten unter einer Lesesperre. Wenn ein Schreibvorgang die Datei währenddessen ändert, beginnt es von vorn. Auf der Festplatte landet dadurch ein konsistenter Zeitpunkt.

Datenkopie mit sqlite3 .backup erstellen

sudo apt update && sudo apt install -y sqlite3
sudo install -d -m 700 /var/backups/vaultwarden
OUT=/var/backups/vaultwarden/db-$(date '+%Y%m%d-%H%M').sqlite3
sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 ".backup '$OUT'"
sudo sqlite3 "$OUT" "PRAGMA integrity_check;"

Der letzte Befehl gibt ok in einer eigenen Zeile aus. Jede andere Ausgabe bedeutet, dass die Kopie nicht verwendbar ist. Behalten Sie sie dann nicht und löschen Sie die vorherige Kopie nicht. Die gesamte Sequenz läuft gegen einen aktiven Server. Dadurch wird niemand abgemeldet und kein Container neu gestartet.

Das Tool sqlite3 befindet sich nicht im Vaultwarden-Container. Das Image basiert auf debian:trixie-slim und enthält ca-certificates, curl, libmariadb3, libpq5 sowie openssl. Daher schlägt docker exec vaultwarden sqlite3 ... mit folgender Meldung fehl:

exec: "sqlite3": executable file not found in $PATH

Führen Sie den Befehl stattdessen auf dem Host gegen den eingebundenen Pfad aus. Genau das machen die obigen Befehle. Wenn die Daten in einem benannten Volume liegen, gibt docker volume inspect <name> den Host-Pfad unter /var/lib/docker/volumes/ aus.

Vaultwarden liefert seit Version 1.32.1 außerdem einen eigenen Backup-Befehl. Führen Sie ihn auf Ihrem Server aus:

docker exec -it vaultwarden /vaultwarden backup

Der Befehl führt VACUUM INTO aus und schreibt db_YYYYMMDD_HHMMSS.sqlite3 in den Datenordner. Daraus ergeben sich zwei Punkte. Die Kopie liegt auf demselben Datenträger neben dem Original. Daher ist sie zunächst nur ein Bereitstellungsschritt und noch kein Backup. Außerdem funktioniert der Befehl nur mit SQLite. Bei MariaDB oder PostgreSQL wird er mit The database type is not SQLite. Backups only works for SQLite databases beendet.

Die Dateien, die häufig vergessen werden

attachments/ enthält Chiffretext unter nichtssagenden Namen. Die Datenbankzeile jedes Anhangs enthält seinen verschlüsselten Dateinamen und das Schlüsselmaterial, das ein Client zum Entschlüsseln der Datei benötigt. Anhänge ohne Datenbank sind unlesbarer Datenmüll. Eine Datenbank ohne Anhänge enthält für Benutzer Einträge, deren Downloads fehlschlagen. Sichern Sie beides im selben Lauf.

config.json enthält alles, was Sie auf der Administrationsseite gespeichert haben. Die dort enthaltenen Werte haben Vorrang vor den entsprechenden Umgebungsvariablen. Das kann sich in beide Richtungen auswirken: Beim Wiederherstellen einer alten config.json werden die Einstellungen in Ihrer Compose-Datei unbemerkt überschrieben. Außerdem ist die Datei selbst vertraulich, weil sie Ihr SMTP-Passwort und Ihr Admin-Token enthalten kann. Speichern Sie dieses Token als Argon2id-PHC-Zeichenfolge (Password Hashing Competition) und nicht als Klartext. docker run --rm -it vaultwarden/server /vaultwarden hash gibt eine solche Zeichenfolge aus.

rsa_key.pem signiert die JSON Web Tokens (JWT), mit denen Clients angemeldet bleiben. Fehlt die Datei beim Start, erzeugt Vaultwarden einen neuen Schlüssel. Dadurch werden alle mit dem alten Schlüssel signierten Tokens ungültig, und alle Clients werden abgemeldet. Die Inhalte des Vault bleiben erhalten, weil sie mit aus dem Master-Passwort abgeleiteten Schlüsseln verschlüsselt sind. Durch das Wiederherstellen der Schlüsseldatei vermeiden Sie die Abmeldung aller Clients.

sends/ enthält die Dateien hinter Send-Links. Fehlen diese Dateien, funktionieren die entsprechenden Downloads nicht. Andere Funktionen sind nicht betroffen.

Das Ganze in ein Skript packen

#!/bin/bash
set -euo pipefail

DATA=/opt/vaultwarden/data
DEST=/var/backups/vaultwarden
STAMP=$(date '+%Y%m%d-%H%M%S')
STAGE=$(mktemp -d /tmp/vw-stage.XXXXXX)

install -d -m 700 "$DEST"
sqlite3 "$DATA/db.sqlite3" ".backup '$STAGE/db.sqlite3'"
test "$(sqlite3 "$STAGE/db.sqlite3" 'PRAGMA integrity_check;')" = "ok"
cp -a "$DATA"/rsa_key* "$STAGE/"
for extra in config.json attachments sends; do
  if [ -e "$DATA/$extra" ]; then cp -a "$DATA/$extra" "$STAGE/"; fi
done
tar -C "$STAGE" -czf "$DEST/vw-$STAMP.tar.gz" .
chmod 600 "$DEST/vw-$STAMP.tar.gz"
rm -rf "$STAGE"
tar -tzf "$DEST/vw-$STAMP.tar.gz"

Speichern Sie das Skript als /usr/local/sbin/vw-backup.sh, machen Sie es mit chmod 700 ausführbar und führen Sie es als root aus. Die Zeile test erledigt die eigentliche Arbeit: sqlite3 beendet sich auch dann mit Exit-Status 0, wenn PRAGMA integrity_check eine Beschädigung meldet. Erst der Vergleich der Ausgabe mit ok sorgt dafür, dass eine fehlerhafte Kopie als fehlgeschlagenes Skript behandelt wird. set -euo pipefail bricht anschließend alles ab, statt tar ein scheinbar korrektes Archiv um eine beschädigte Datenbank herum zu erstellen.

tar -tzf listet am Ende auf, was tatsächlich erfasst wurde. Lesen Sie diese Ausgabe beim ersten Mal vollständig. Achten Sie auf ./db.sqlite3, ./rsa_key.pem, ./config.json und ./attachments/ sowie darauf, dass ./db.sqlite3-wal nicht vorhanden ist. Führen Sie das Skript nachts mit einem systemd-Service und Timer statt mit cron aus, wenn Sie eine journalctl-Ausgabe und eine Unit wünschen, die Fehler meldet.

Sichern Sie das Backup durch eine Wiederherstellung in ein temporäres Verzeichnis

Ein nicht getestetes Backup ist nur eine Vermutung. Die Wiederherstellung in ein temporäres Verzeichnis dauert eine Minute und verändert keine produktiven Daten.

sudo install -d -m 700 /tmp/vw-check
sudo tar -C /tmp/vw-check -xzf /var/backups/vaultwarden/vw-20260805-030000.tar.gz
ls -l /tmp/vw-check
sudo sqlite3 /tmp/vw-check/db.sqlite3 "PRAGMA integrity_check;"
sudo sqlite3 /tmp/vw-check/db.sqlite3 "select count(*) from users;"
sudo sqlite3 /tmp/vw-check/db.sqlite3 "select count(*) from ciphers;"
sudo du -sh /tmp/vw-check/attachments

Vier Ergebnisse sind relevant. integrity_check gibt ok aus. Die Benutzeranzahl entspricht der Zahl der Ihnen bekannten Konten. Die Anzahl der Chiffren liegt nahe am aktiven Wert aus sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 "select count(*) from ciphers;" und ist bei einem verwendeten Tresor niemals null. Das Verzeichnis für Anhänge hat ungefähr die erwartete Größe. Diesen Check können Sie überspringen, wenn niemand Anhänge hochlädt. Führen Sie anschließend sudo rm -rf /tmp/vw-check aus, weil dieses Verzeichnis nun eine zweite Kopie aller Daten enthält.

Beachten Sie bei der Wiederherstellung eines manuell kopierten Datenverzeichnisses eine Regel: Löschen Sie db.sqlite3-wal und db.sqlite3-shm, bevor Sie den Server starten. SQLite versucht andernfalls, die wiederhergestellte Datenbank mit einem Log wiederherzustellen, das zu einer anderen Kopie gehört. Dadurch wird eine intakt eingetroffene Datenbank beschädigt. Die vom vorherigen Skript erzeugten Archive enthalten diese Dateien nie, weil .backup eine vollständige Datenbank schreibt.

Wiederherstellung auf dem Server

Führen Sie diese Befehle auf Ihrem eigenen Server aus, während der Container gestoppt ist. Vaultwarden darf nicht schreiben, während der Datenordner geändert wird.

cd /opt/vaultwarden
docker compose stop vaultwarden
sudo mv data data.old.$(date '+%Y%m%d-%H%M%S')
sudo install -d -m 700 data
sudo tar -C data -xzf /var/backups/vaultwarden/vw-20260805-030000.tar.gz
sudo chown -R root:root data
docker compose start vaultwarden
docker compose logs --tail 20 vaultwarden

chown muss den Benutzer angeben, unter dem der Container ausgeführt wird. Das Standardimage wird als root ausgeführt. Daher ist root:root korrekt, sofern Sie user: nicht in Ihrer Compose-Datei gesetzt haben. Verwenden Sie in diesem Fall die dort angegebene UID und GID. Wenn der Server nicht in den Datenordner schreiben kann, wird zwar eine Anmeldeseite angezeigt, aber jede Anfrage schlägt fehl. Das ist auch in den Logs zu sehen.

Ein erfolgreicher Start endet mit der Rocket-Zeile:

[INFO] Rocket has launched from http://0.0.0.0:80

Melden Sie sich anschließend im Browser an, öffnen Sie ein Element und laden Sie einen Anhang herunter. Wenn die Anmeldung funktioniert, aber der Download von Anhängen fehlschlägt, enthält das Archiv zwar die Datenbank, aber nicht attachments/. Behalten Sie data.old.*, bis alle Prüfungen erfolgreich waren, und löschen Sie es erst danach. Für ein Rollback führen Sie dieselben drei Schritte aus und vertauschen dabei die Verzeichnisse.

Wenn Ihre Pfade nicht mit den hier verwendeten übereinstimmen, zeigt die Vaultwarden-Installationsanleitung für einen VPS die Compose-Datei, von der diese Befehle ausgehen.

Wohin die Sicherung nicht gehört

  • Nicht auf dieselbe Festplatte wie der Datenordner. Ein ausgefallenes Volume zerstört beide Kopien. Das gilt auch für ein rm -rf am falschen Pfad.
  • Nicht auf denselben Server, auch nicht auf einem zweiten Volume. Ein Angreifer, der root erreicht, erreicht in derselben Sitzung auch Ihre Sicherungen.
  • Nicht unverschlüsselt in einem Object Storage, weil das Archiv E-Mail-Adressen, Hinweise auf Passwörter, Wiederherstellungscodes und Vault-Chiffretext enthält, die offline angegriffen werden können.
  • Nicht ausschließlich in den Snapshots Ihres Providers. Sie lassen sich schnell wiederherstellen, was nützlich ist. Sie befinden sich jedoch im selben Konto wie der Server. Ein Kontoproblem betrifft daher auch die Snapshots.

Für eine Offsite-Kopie eignet sich restic, weil ein restic-Repository auf dem Rechner verschlüsselt wird, bevor Daten hochgeladen werden. Auf Ihrem Server:

sudo apt install -y restic
export RESTIC_REPOSITORY=s3:https://s3.example.com/vaultwarden-backups
export RESTIC_PASSWORD_FILE=/root/.restic-password
restic init
restic backup /var/backups/vaultwarden --tag vaultwarden
restic snapshots --tag vaultwarden
restic forget --tag vaultwarden --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

Verweisen Sie restic auf das Archivverzeichnis und nicht auf den aktiven Datenordner. Dadurch wird die konsistente Kopie hochgeladen, die Sie bereits geprüft haben. Bewahren Sie das Repository-Passwort an einem anderen Ort als auf dem Server auf, den es schützt. Wenn Sie das Passwort verlieren, sind die Snapshots absichtlich nicht lesbar. Wenn der Speicher dies unterstützt, sollten die Zugangsdaten des Servers Schreibrechte, aber keine Löschrechte haben. So kann ein Angreifer bei einer Kompromittierung des Servers nicht dessen eigene Historie löschen. restic-Sicherungen auf einem VPS einrichten behandelt Repository und Zeitplan vollständig. restic im Vergleich zu BorgBackup hilft bei der Auswahl, falls Sie sich noch nicht entschieden haben.

Testen Sie die Wiederherstellung nach einem festen Zeitplan

Wählen Sie einen Tag im Monat. Laden Sie den neuesten Snapshot mit restic restore latest --tag vaultwarden --target /tmp/vw-check in ein temporäres Verzeichnis herunter, führen Sie dasselbe PRAGMA integrity_check aus, ermitteln Sie dieselben Zeilenzahlen und dokumentieren Sie anschließend das Datum und die Zählwerte. Ein Backup, das seit sechs Monaten niemand wiederhergestellt hat, befindet sich in einem unbekannten Zustand. Diesen Zustand erfahren Sie dann während eines Ausfalls. Das ist der ungünstigste Zeitpunkt dafür.

Einmal pro Jahr führen Sie den vollständigen Test durch. Starten Sie mit dem wiederhergestellten Datenverzeichnis einen zweiten Vaultwarden-Container an einem freien Port und melden Sie sich mit einem echten Konto an. Damit prüfen Sie den Pfad des Master-Passworts Ende zu Ende. Eine Zeilenzählung kann das nicht leisten. restic check --read-data-subset=10% nach demselben Zeitplan stellt sicher, dass die gespeicherten Daten lesbar sind und nicht nur aufgelistet werden.

FAQ

Kann ich db.sqlite3 mit cp kopieren, während Vaultwarden läuft?

Nein. Vaultwarden verwendet SQLite im WAL-Modus. Aktuelle Schreibvorgänge liegen daher in db.sqlite3-wal und noch nicht in db.sqlite3. Eine cp ausschließlich der Hauptdatei verliert diese Daten unbemerkt. Werden die beiden Dateien separat kopiert, kann ein inkonsistentes Paar entstehen, das später als Error: database disk image is malformed sichtbar wird. Verwenden Sie stattdessen sqlite3 /path/db.sqlite3 ".backup '/path/out.sqlite3'". Dieser Befehl verwendet die Online Backup API von SQLite und erstellt eine konsistente Datei, während der Server weiterhin Anfragen verarbeitet.

Muss ich den Vaultwarden-Container für ein Backup stoppen?

Nein. Genau das ist der Zweck von .backup. Die Datenbank kann sicher auf einem laufenden Server kopiert werden. Anhänge und Send-Dateien werden beim Upload durch einen Benutzer geschrieben. Wird eine Datei zwischen der Datenbankkopie und dem tar hinzugefügt, kann sie im Archiv dieser Nacht fehlen. Im schlimmsten Fall verlieren Sie dadurch einen Anhang. Wenn einige Sekunden Ausfallzeit akzeptabel sind, entfernt docker compose stop vor dem Skript und docker compose start danach auch dieses Risiko.

Was passiert, wenn ich die Sicherung ohne die rsa_key-Dateien wiederherstelle?

Vaultwarden erzeugt beim Start einen neuen Schlüssel. Dieser Schlüssel signiert die JSON Web Tokens (JWT), die Sitzungen aktiv halten. Daher können vorhandene Tokens nicht mehr validiert werden. Alle Clients werden abgemeldet und müssen sich erneut anmelden. Die Tresorinhalte bleiben unverändert, weil sie mit Schlüsseln verschlüsselt sind, die aus dem Master-Passwort des jeweiligen Benutzers abgeleitet werden, und nicht mit dem RSA-Schlüssel. Stellen Sie rsa_key.pem zusammen mit dem restlichen Datenverzeichnis wieder her. Dann bemerkt niemand die Wiederherstellung.

Ist das Backup-Archiv in unveränderter Form für den Upload in einen Objektspeicher geeignet?

Nein. Elementnamen, Passwörter und Notizen sind Chiffretext. E-Mail-Adressen, Kontonamen, Passwort-Hinweise und Wiederherstellungscodes für die Zwei-Faktor-Authentifizierung liegen jedoch als Klartext in der Datenbank vor. Ein Angreifer kann den Chiffretext außerdem offline und mit beliebigem Zeitaufwand analysieren. Verschlüsseln Sie das Archiv, bevor es den Server verlässt. Ein restic-Repository übernimmt das für Sie. gpg --symmetric --cipher-algo AES256 vw-20260805-030000.tar.gz erstellt eine einzelne verschlüsselte Datei, die Sie an beliebigen Speicher übergeben können.

Wie sichere ich Vaultwarden mit PostgreSQL oder MariaDB?

Die Schritte für SQLite gelten hier nicht. Der integrierte Befehl verweigert die Ausführung mit The database type is not SQLite. Backups only works for SQLite databases. Sichern Sie die Datenbank mit dem jeweiligen nativen Werkzeug, pg_dump oder mysqldump, und befolgen Sie alle anderen Regeln unverändert. Der Dump gehört zusammen mit attachments/, sends/, config.json und den rsa_key-Dateien in ein gemeinsames Archiv. Alle Dateien müssen im selben Durchlauf erstellt, verschlüsselt und an einem anderen Ort als dem Server gespeichert werden, auf dem die Sicherung erstellt wurde.