SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-28

Vaultwarden back-up maken en herstellen op een VPS

Leer hoe u een veilige Vaultwarden back-up maakt met sqlite3 .backup. Zorg dat u db.sqlite3, config.json en de attachments map correct kopieert om dataverlies te voorkomen.

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, August 5, 2026.

Wat een Vaultwarden-back-up moet bevatten

Een Vaultwarden-back-up is een kopie van de volledige datamap, waarbij de database op de juiste wijze moet worden gekopieerd. Voer sqlite3 db.sqlite3 ".backup out.sqlite3" uit in plaats van cp, omdat een eenvoudige kopie van een database die op dat moment wordt beschreven, kan resulteren in een bestand dat niet kan worden geopend. Bewaar vervolgens de bestanden die zich daarnaast bevinden; dit is het onderdeel dat vaak wordt vergeten.

Bij een Docker-installatie is de datamap de locatie die u heeft gemount op /data. Dit is ofwel een pad op de host of een named volume, en het verschil tussen bind mounts en named volumes bepaalt waar uw vault zich daadwerkelijk op de schijf bevindt. Dit is wat de map bevat:

  • db.sqlite3: elk account, elk vault-item, elke map en elke organisatie. Als u dit bestand verliest, verliest u de vault.
  • db.sqlite3-wal en db.sqlite3-shm: het write-ahead log (WAL) en de bijbehorende shared memory index. Recente schrijfacties bevinden zich hier totdat SQLite ze samenvoegt in het hoofdbestand.
  • attachments/: de bestanden die gebruikers aan vault-items hebben toegevoegd, versleuteld, in één map per item.
  • sends/: de bestanden achter Bitwarden Send-links.
  • config.json: alle instellingen die u heeft opgeslagen via de admin-pagina.
  • rsa_key.pem, plus rsa_key.der en rsa_key.pub.der bij oudere installaties: de sleutel die inlogtokens ondertekent.
  • icon_cache/: gedownloade website-iconen. Dit is de enige map die u kunt overslaan, omdat Vaultwarden deze op verzoek opnieuw ophaalt.

Is mijn Vaultwarden-database veilig? Wat het bestand werkelijk bevat

Twee commando's geven hierop het antwoord, en u kunt beide direct uitvoeren.

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;"

Het eerste commando toont de e-mailadressen van uw gebruikers in leesbare tekst. Het tweede toont één itemnaam, die er als volgt uitziet:

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

Itemnamen, gebruikersnamen, wachtwoorden en notities worden door de client versleuteld voordat ze worden verzonden. De server slaat dus ciphertext op die hij niet kan lezen. Het 2.-voorvoegsel is het versleutelingstype van Bitwarden, gevolgd door een initialisation vector (IV), de ciphertext en een MAC (message authentication code), elk in base64 en gescheiden door |. De sleutel die dit ontsleutelt, wordt afgeleid van het hoofdwachtwoord van het account, dat de server nooit in bruikbare vorm bereikt. Dit onderdeel is identiek, ongeacht of u Vaultwarden of de officiële server draait, zoals de vergelijking tussen Vaultwarden en zelf-gehoste Bitwarden beschrijft.

De rest van de database is niet versleuteld. E-mailadressen, accountnamen, wachtwoordhints en herstelcodes voor tweefactorauthenticatie worden opgeslagen als platte tekst, naast metadata zoals aanmaaktijden en de organisatie die eigenaar is van een item. Het back-upbestand is dus zelf een geheim. Iedereen die het in handen krijgt, weet wie uw gebruikers zijn en kan de versleutelde blobs offline aanvallen met de snelheid die hun hardware toelaat. Dat enkele feit bepaalt de opslagregels verderop: de kopie wordt versleuteld voordat deze de server verlaat. Het admin-token is de andere helft van hetzelfde probleem, en de hardening-handleiding voor een zelf-gehoste Vaultwarden behandelt beide aspecten.

Waarom het kopiëren van db.sqlite3 terwijl Vaultwarden draait geen back-up is

Vaultwarden gebruikt standaard SQLite in WAL-modus (ENABLE_DB_WAL=true). Een schrijfactie komt eerst terecht in db.sqlite3-wal en wordt pas na een checkpoint samengevoegd in db.sqlite3. Kopieert u alleen db.sqlite3, dan krijgt u de database zoals deze was bij het laatste checkpoint. Een wachtwoord dat tien minuten geleden is opgeslagen, kan dus ontbreken in uw archief zonder dat u hiervoor een waarschuwing krijgt.

Het kopiëren van alle drie de bestanden met cp is evenmin een oplossing. De kopieën worden op licht verschillende momenten gemaakt, waardoor de WAL die u heeft opgeslagen paginavariaties kan beschrijven die niet langer overeenkomen met het hoofdbestand dat u heeft opgeslagen. SQLite probeert vervolgens het een met het ander te herstellen, wat resulteert in een foutieve database. U komt hier pas veel later achter:

Error: database disk image is malformed

.backup voorkomt dit omdat het gebruikmaakt van de Online Backup API van SQLite. SQLite documenteert dit als de juiste methode om een database te kopiëren die mogelijk actief in gebruik is. Het leest de pagina's onder een read lock en begint opnieuw als een schrijfactie het bestand tussentijds wijzigt. Wat uiteindelijk op de schijf wordt opgeslagen, is daardoor één consistent momentopname.

Maak een databasekopie met sqlite3 .backup

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;"

Het laatste commando toont ok op een eigen regel. Elke andere uitvoer betekent dat de kopie niet bruikbaar is; bewaar deze dan niet en verwijder de vorige kopie niet. De gehele reeks wordt uitgevoerd op een actieve server, waardoor niemand wordt uitgelogd en er geen containers worden herstart.

De tool sqlite3 bevindt zich niet in de Vaultwarden-container. De image is gebouwd op debian:trixie-slim met ca-certificates, curl, libmariadb3, libpq5 en openssl, waardoor docker exec vaultwarden sqlite3 ... faalt met:

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

Voer het commando in plaats daarvan uit op de host tegen het gemounte pad, zoals de bovenstaande commando's doen. Als de data zich in een named volume bevindt, toont docker volume inspect <name> het hostpad onder /var/lib/docker/volumes/.

Vaultwarden bevat sinds versie 1.32.1 ook een eigen backup-commando. Op uw server:

docker exec -it vaultwarden /vaultwarden backup

Dit voert VACUUM INTO uit en schrijft db_YYYYMMDD_HHMMSS.sqlite3 naar de datamap. Hieruit volgen twee zaken. De kopie wordt naast het origineel op dezelfde schijf geplaatst, dus dit is een staging-stap en nog geen backup. Bovendien werkt dit alleen voor SQLite: bij MariaDB of PostgreSQL stopt het proces met The database type is not SQLite. Backups only works for SQLite databases.

Bestanden die vaak worden vergeten

attachments/ bevat versleutelde gegevens onder onleesbare bestandsnamen. De databaserij voor elke bijlage bevat de versleutelde bestandsnaam en het sleutelmateriaal dat een client nodig heeft om het bestand te ontsleutelen. Bijlagen zonder de database zijn onleesbare ruis, en een database zonder de bijlagen levert gebruikers items op waarvan de downloads mislukken. Neem beide mee in dezelfde back-up.

config.json bevat alles wat u hebt opgeslagen via de beheerpagina, en de waarden hierin krijgen voorrang op de overeenkomstige omgevingsvariabelen. Dit werkt twee kanten op: het herstellen van een oude config.json overschrijft stilletjes de instellingen in uw compose-bestand, en het bestand zelf is gevoelig omdat het uw SMTP-wachtwoord en uw admin-token kan bevatten. Sla dat token op als een Argon2id PHC-string (password hashing competition) in plaats van als platte tekst. docker run --rm -it vaultwarden/server /vaultwarden hash genereert er een voor u.

rsa_key.pem ondertekent de JSON web tokens (JWT) die ervoor zorgen dat clients ingelogd blijven. Als het bestand bij het opstarten ontbreekt, genereert Vaultwarden een nieuwe sleutel, waardoor elk token dat door de oude sleutel is ondertekend niet langer valideert en alle clients worden uitgelogd. De inhoud van de Vault blijft behouden, omdat deze is versleuteld met sleutels die zijn afgeleid van het hoofdwachtwoord. Het herstellen van het sleutelbestand voorkomt dat iedereen massaal wordt uitgelogd.

sends/ bevat de bestanden achter Send-links. Als deze ontbreken, werken die downloads niet meer, maar heeft dit verder geen gevolgen.

Voeg alles samen in één script

#!/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"

Sla het op als /usr/local/sbin/vw-backup.sh, maak het chmod 700 en voer het uit als root. De regel test voert het eigenlijke werk uit: sqlite3 geeft exitcode 0 terug, zelfs wanneer PRAGMA integrity_check corruptie rapporteert. Daarom is het vergelijken van de output met ok de methode om een slechte kopie te laten resulteren in een mislukt script. set -euo pipefail stopt vervolgens het hele proces, in plaats van tar toe te staan een archief op te bouwen rondom een defecte database.

Het laatste tar -tzf toont wat u daadwerkelijk heeft vastgelegd. Controleer dit de eerste keer. U zoekt naar ./db.sqlite3, ./rsa_key.pem, ./config.json en ./attachments/, en naar de afwezigheid van ./db.sqlite3-wal. Voer het script dagelijks uit met een systemd service en timer in plaats van cron als u journalctl output en een unit die fouten rapporteert wenst.

Verifieer de back-up door deze terug te zetten in een tijdelijke map

Een ongeteste back-up is slechts een aanname. Het terugzetten naar een tijdelijke map kost een minuut en heeft geen invloed op de live-omgeving.

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 resultaten zijn van belang. integrity_check voert ok uit. Het aantal gebruikers komt overeen met het aantal accounts dat u kent. Het aantal ciphers ligt dicht bij het live-cijfer van sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 "select count(*) from ciphers;" en is nooit nul bij een kluis die in gebruik is. De map met bijlagen heeft ongeveer de verwachte grootte; dit kunt u overslaan als er geen bijlagen worden geüpload. Voer daarna sudo rm -rf /tmp/vw-check uit, omdat die map nu een tweede kopie van alles bevat.

Hanteer één regel wanneer u een handmatig gekopieerde datamap terugzet: verwijder db.sqlite3-wal en db.sqlite3-shm voordat u de server start. SQLite zal anders proberen de herstelde database te herstellen met behulp van een logbestand dat bij een andere kopie hoort, waardoor een intacte database beschadigd raakt. Archieven die door het bovenstaande script worden gemaakt, bevatten deze bestanden nooit, omdat .backup één volledige database schrijft.

Herstellen op de server

Deze acties voert u uit op uw eigen server, terwijl de container is gestopt. Vaultwarden mag niet schrijven terwijl de datamap eronder wordt gewijzigd.

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

De chown moet de gebruiker benoemen waaronder de container draait. De standaard image draait als root, dus root:root is correct, tenzij u user: heeft ingesteld in uw compose-bestand; gebruik in dat geval die uid en gid. Een datamap waar de server niet naar kan schrijven, resulteert in een inlogpagina die elk verzoek afwijst, wat ook in de logs wordt vermeld.

Een gezonde start eindigt met de Rocket-regel:

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

Log daarna in via een browser, open een item en download een bijlage. Een inlog die werkt terwijl het downloaden van bijlagen faalt, betekent dat het archief de database wel bevatte, maar attachments/ niet. Bewaar data.old.* totdat alles is gecontroleerd en verwijder het daarna pas. Terugdraaien gebeurt via dezelfde drie stappen, waarbij de mappen in omgekeerde volgorde worden gebruikt.

Als uw paden niet overeenkomen met de paden in dit document, toont de Vaultwarden installatiehandleiding voor een VPS het compose-bestand waarvan deze commando's uitgaan.

Waar u back-ups niet moet opslaan

  • Niet op dezelfde schijf als de datamap. Eén defect volume vernietigt beide kopieën, net als een rm -rf op het verkeerde pad.
  • Niet op dezelfde server, zelfs niet op een tweede volume. Een aanvaller die root-toegang verkrijgt, bereikt uw back-ups in dezelfde sessie.
  • Niet in object storage zonder versleuteling, omdat het archief e-mailadressen, wachtwoordhints, herstelcodes en vault-ciphertext bevat die offline kunnen worden aangevallen.
  • Niet uitsluitend in de snapshots van uw provider. Deze zijn snel te herstellen, wat nuttig is, maar ze bevinden zich in hetzelfde account als de server; een probleem met het account treft dus ook de snapshots.

Een offsite kopie is waar restic van pas komt, omdat een restic-repository op de machine wordt versleuteld voordat er iets wordt geüpload. Op uw 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

Wijs restic aan naar de archiefmap en niet naar de actieve datamap, zodat wat wordt geüpload de consistente kopie is die u al heeft gecontroleerd. Bewaar het wachtwoord van de repository ergens anders dan op de server die het beschermt: verliest u dat wachtwoord, dan zijn de snapshots volgens ontwerp onleesbaar. Waar de opslag dit ondersteunt, geeft u de server inloggegevens waarmee wel geschreven maar niet verwijderd kan worden, zodat een inbreuk op de server de eigen geschiedenis niet kan wissen. Het instellen van restic-back-ups op een VPS behandelt de repository en de planning volledig, en restic vergeleken met BorgBackup behandelt de keuze als u deze nog niet heeft gemaakt.

Test het herstel volgens een schema

Kies één dag per maand. Haal de nieuwste snapshot op naar een tijdelijke map met restic restore latest --tag vaultwarden --target /tmp/vw-check, voer hetzelfde PRAGMA integrity_check uit, voer dezelfde rij-tellingen uit en noteer vervolgens de datum en de aantallen. Een back-up die in zes maanden niet is hersteld, is een back-up met een onbekende status. U komt pas achter deze status tijdens een storing, wat het slechtste moment is om dit te ontdekken.

Voer eenmaal per jaar de volledige procedure uit. Start een tweede Vaultwarden container op een vrije poort met de herstelde datamap en log in met een echt account. Dit bewijst dat het pad voor het hoofdwachtwoord volledig functioneert, wat met een rij-telling niet kan worden vastgesteld. restic check --read-data-subset=10% op hetzelfde schema verifieert dat de opgeslagen gegevens leesbaar zijn in plaats van alleen in de lijst staan.

FAQ

Kan ik db.sqlite3 kopiëren met cp terwijl Vaultwarden draait?

Nee. Vaultwarden gebruikt SQLite in WAL-modus, waardoor recente schrijfacties in db.sqlite3-wal staan en nog niet in db.sqlite3 zijn verwerkt. Een cp van alleen het hoofdbestand zorgt ervoor dat deze gegevens ongemerkt verloren gaan. Het afzonderlijk kopiëren van de twee bestanden kan leiden tot een inconsistent paar, wat later resulteert in Error: database disk image is malformed. Gebruik in plaats daarvan sqlite3 /path/db.sqlite3 ".backup '/path/out.sqlite3'". Dit commando maakt gebruik van de Online Backup API van SQLite en genereert één consistent bestand terwijl de server actief blijft.

Moet ik de Vaultwarden-container stoppen om een back-up te maken?

Nee, dat is juist het doel van .backup. De databasekopie is veilig op een draaiende server. Bijlagen en Send-bestanden worden geschreven op het moment dat een gebruiker ze uploadt. Een bestand dat wordt toegevoegd tussen de databasekopie en de tar kan in het slechtste geval ontbreken in het archief van die nacht. Als een paar seconden downtime geen probleem is, zorgt docker compose stop voor het script en docker compose start erna ervoor dat ook dit risico wordt geëlimineerd.

Wat gebeurt er als ik terugzet zonder de rsa_key-bestanden?

Vaultwarden genereert bij het opstarten een nieuwe sleutel. Deze sleutel ondertekent de JSON web tokens (JWT) die sessies actief houden. Hierdoor worden alle bestaande tokens ongeldig, worden alle clients uitgelogd en moeten zij opnieuw inloggen. De inhoud van de kluis blijft onaangetast, omdat deze is versleuteld met sleutels die zijn afgeleid van het hoofdwachtwoord van elke gebruiker, in plaats van met de RSA-sleutel. Herstel rsa_key.pem samen met de rest van de datamap en niemand merkt iets van het herstel.

Is het back-uparchief veilig om zo te uploaden naar object storage?

Nee. Itemnamen, wachtwoorden en notities zijn ciphertext, maar e-mailadressen, accountnamen, wachtwoordhints en herstelcodes voor tweefactorauthenticatie staan in platte tekst in de database. Een aanvaller die offline toegang krijgt, kan de ciphertext in eigen tempo kraken. Versleutel het archief voordat het de machine verlaat. Een restic-repository doet dit voor u, en gpg --symmetric --cipher-algo AES256 vw-20260805-030000.tar.gz genereert één versleuteld bestand dat u naar elke opslaglocatie kunt sturen.

Hoe maak ik een back-up van Vaultwarden op PostgreSQL of MariaDB?

De stappen voor SQLite zijn hier niet van toepassing en het ingebouwde commando zal weigeren met The database type is not SQLite. Backups only works for SQLite databases. Maak een dump van de database met het bijbehorende native hulpprogramma, pg_dump of mysqldump, en hanteer voor de rest dezelfde regels. De dump hoort in één archief thuis met attachments/, sends/, config.json en de rsa_key-bestanden. Zorg dat deze in dezelfde run worden meegenomen, versleuteld worden en op een andere locatie dan de bronserver worden opgeslagen.