SSD Nodes Learn 🎉 VPS vanaf $5.50/mnd
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-13

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, attachments, config.json en rsa_key bestanden correct kopieert voor een volledig herstel.

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 kluis zich daadwerkelijk op de schijf bevindt. Dit is wat de map bevat:

  • db.sqlite3: elk account, elk kluisitem, elke map en elke organisatie. Als u dit bestand verliest, verliest u de kluis.
  • 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 kluisitems hebben gekoppeld, versleuteld, in één map per item.
  • sends/: de bestanden achter Bitwarden Send-links.
  • config.json: elke instelling die u heeft opgeslagen via de beheerpagina.
  • 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 hier antwoord op, 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 de naam van één item, en dat ziet er als volgt uit:

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

Itemnamen, gebruikersnamen, wachtwoorden en notities worden door de client versleuteld voordat ze worden verzonden, waardoor de server ciphertext opslaat 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 toelicht.

De rest van de database is niet versleuteld. E-mailadressen, accountnamen, wachtwoordhints en herstelcodes voor tweefactorauthenticatie worden als platte tekst opgeslagen, 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 ene feit bepaalt de opslagregels verderop: de kopie wordt versleuteld voordat deze de server verlaat.

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 pas na een checkpoint wordt deze 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 paginiversies kan beschrijven die niet meer overeenkomen met het hoofdbestand dat u heeft opgeslagen. SQLite probeert vervolgens de bestanden met elkaar te herstellen, wat resulteert in een corrupte 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 schrijvend proces het bestand ondertussen wijzigt. Wat uiteindelijk op de schijf terechtkomt, is dus één consistent momentopname.

Maak een kopie van de database 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 print ok op een eigen regel. Elke andere uitvoer betekent dat de kopie niet bruikbaar is; bewaar deze niet en verwijder de vorige kopie niet. De gehele reeks wordt uitgevoerd op een actieve server, waardoor niemand wordt uitgelogd en er geen containers herstarten.

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 in de bovenstaande commando's wordt getoond. Als de data zich in een named volume bevindt, print 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; dit is dus een tussenstap en nog geen volwaardige 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 via de beheerpagina heeft opgeslagen, en de waarden hierin hebben 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 kluis blijft behouden, omdat deze is versleuteld met sleutels die zijn afgeleid van het hoofdwachtwoord. Het herstellen van het sleutelbestand voorkomt dat alle gebruikers tegelijk worden uitgelogd.

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

Het geheel in één script plaatsen

#!/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 een exitcode 0, zelfs wanneer PRAGMA integrity_check corruptie rapporteert. Daarom is het vergelijken van de output met ok de manier om een slechte kopie om te zetten in een gefaald script. set -euo pipefail stopt vervolgens alles, in plaats van tar toe te staan een net archief rondom een defecte database te bouwen.

De 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 's nachts uit met een systemd service en timer in plaats van cron als u journalctl-output wilt en een unit die fouten rapporteert.

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

Een ongeteste back-up is slechts een aanname. Het terugzetten in 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 geeft ok weer. 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 vault 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 bij het terugzetten van handmatig gekopieerde datamappen: verwijder db.sqlite3-wal en db.sqlite3-shm voordat u de server start. SQLite probeert anders de herstelde database te herstellen met een logbestand dat bij een andere kopie hoort, waardoor een intacte database corrupt raakt. Archieven die met het bovenstaande script zijn gemaakt, bevatten deze bestanden nooit, omdat .backup één complete 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 verloopt via dezelfde drie stappen, waarbij de mappen worden omgewisseld.

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 in dezelfde sessie ook uw back-ups.
  • Niet in object storage zonder versleuteling, omdat het archief e-mailadressen, wachtwoordhints, herstelcodes en kluis-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 back-ups.

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 er 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 machine 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 die 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 rijtellingen 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 reservepoort met de herstelde gegevensmap en log in met een echt account. Dit bewijst het volledige pad van het hoofdwachtwoord, wat met geen enkele rijtelling kan worden aangetoond. restic check --read-data-subset=10% volgens hetzelfde schema verifieert dat de opgeslagen gegevens leesbaar zijn in plaats van alleen in een lijst te 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 voor gegevensverlies, en het afzonderlijk kopiëren van de twee bestanden kan leiden tot een mismatch die later als Error: database disk image is malformed naar voren komt. Gebruik in plaats daarvan sqlite3 /path/db.sqlite3 ".backup '/path/out.sqlite3'". Dit maakt gebruik van de Online Backup API van SQLite en genereert één consistent bestand terwijl de server in bedrijf blijft.

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

Nee, en dat is precies 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 zou in het ergste geval kunnen ontbreken in het archief van die nacht. Als een paar seconden downtime geen probleem is, voorkomt docker compose stop vóór het script en docker compose start erna zelfs dat kleine risico.

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

Vaultwarden genereert bij het opstarten een nieuwe sleutel. Die sleutel ondertekent de JSON web tokens (JWT) die sessies actief houden, waardoor elk bestaand token ongeldig wordt, alle clients worden uitgelogd en opnieuw moeten 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 dat er een herstel heeft plaatsgevonden.

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

Nee. Itemnamen, wachtwoorden en notities zijn cijfertekst, maar e-mailadressen, accountnamen, wachtwoordhints en herstelcodes voor tweefactorauthenticatie staan in platte tekst in de database. Een aanvaller die offline toegang heeft, kan de cijfertekst in eigen tempo kraken. Versleutel het archief voordat het de machine verlaat. Een restic-repository doet dit automatisch 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 de bijbehorende tool, pg_dump of mysqldump, en houd u voor de rest aan dezelfde regels. De dump hoort in één archief thuis met attachments/, sends/, config.json en de rsa_key-bestanden, die in dezelfde run worden verwerkt, versleuteld en op een andere locatie dan de server zelf worden opgeslagen.