SSD Nodes Learn 🎉 VPS dès $4.99/mois
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-07

Sauvegarder et restaurer Vaultwarden sur un VPS

Apprenez à sauvegarder Vaultwarden avec sqlite3 .backup, les pièces jointes et les fichiers de configuration, puis vérifiez la restauration avant une panne.

Ce qu’une sauvegarde de Vaultwarden doit contenir

Une sauvegarde de Vaultwarden est une copie de l’intégralité du répertoire de données. La base de données qu’il contient doit être copiée correctement. Exécutez sqlite3 db.sqlite3 ".backup out.sqlite3" au lieu de cp, car la simple copie d’une base de données en cours d’écriture peut produire un fichier impossible à ouvrir. Conservez également les fichiers qui se trouvent à côté. C’est souvent l’élément oublié.

Avec une installation Docker, le répertoire de données correspond à ce que vous avez monté sur /data. Il s’agit soit d’un chemin sur l’hôte, soit d’un volume nommé. La différence entre les bind mounts et les volumes nommés détermine l’emplacement réel de votre coffre sur le disque. Voici ce qu’il contient.

  • db.sqlite3 : tous les comptes, tous les éléments de coffre, tous les dossiers et toutes les organisations. La perte de ce fichier entraîne la perte du coffre.
  • db.sqlite3-wal et db.sqlite3-shm : le write-ahead log (WAL) et son index de mémoire partagée. Les écritures récentes y restent jusqu’à ce que SQLite les intègre au fichier principal.
  • attachments/ : les fichiers joints par les utilisateurs aux éléments du coffre, chiffrés, dans un répertoire par élément.
  • sends/ : les fichiers associés aux liens Bitwarden Send.
  • config.json : tous les paramètres enregistrés depuis la page d’administration.
  • rsa_key.pem, ainsi que rsa_key.der et rsa_key.pub.der sur les anciennes installations : la clé qui signe les tokens de connexion.
  • icon_cache/ : les icônes de sites web téléchargées. C’est le seul répertoire que vous pouvez ignorer, car Vaultwarden les télécharge à nouveau à la demande.

La base de données Vaultwarden est-elle sécurisée ? Ce que contient réellement le fichier

Deux commandes permettent de le vérifier, et vous pouvez les exécuter maintenant.

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

La première affiche en clair les adresses e-mail de vos utilisateurs. La seconde affiche le nom d’un élément, sous cette forme :

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

Les noms d’éléments, les noms d’utilisateur, les mots de passe et les notes sont chiffrés par le client avant leur envoi. Le serveur stocke donc un texte chiffré qu’il ne peut pas lire. Le préfixe 2. indique le type de chiffrement Bitwarden. Il est suivi d’un vecteur d’initialisation (IV), du texte chiffré et d’un MAC (code d’authentification de message), tous encodés en base64 et séparés par |. La clé utilisée pour le déchiffrer est dérivée du mot de passe maître du compte, qui n’est jamais transmis au serveur sous une forme exploitable. Ce fonctionnement est identique avec Vaultwarden et avec le serveur officiel, comme l’explique la comparaison entre Vaultwarden et Bitwarden auto-hébergé.

Le reste de la base de données n’est pas chiffré. Les adresses e-mail, les noms de compte, les indications de mot de passe et les codes de récupération de l’authentification à deux facteurs sont stockés en clair, avec des métadonnées comme les dates de création et l’organisation propriétaire d’un élément. Le fichier de sauvegarde est donc lui-même un secret. Toute personne qui le possède peut savoir qui sont vos utilisateurs et attaquer les données chiffrées hors ligne, à la vitesse permise par son matériel. Ce point détermine les règles de stockage présentées plus bas : la copie est chiffrée avant de quitter le serveur.

Pourquoi copier db.sqlite3 pendant que Vaultwarden fonctionne ne constitue pas une sauvegarde

Vaultwarden utilise SQLite en mode WAL par défaut (ENABLE_DB_WAL=true). Une écriture arrive d’abord dans db.sqlite3-wal, puis un checkpoint l’intègre à db.sqlite3. Si vous copiez uniquement db.sqlite3, vous obtenez la base de données telle qu’elle était lors du dernier checkpoint. Un mot de passe enregistré il y a dix minutes peut donc manquer dans votre archive, sans qu’aucun avertissement ne vous en informe.

Copier les trois fichiers avec cp ne résout pas le problème. Les copies sont effectuées à des instants légèrement différents. Le WAL sauvegardé peut donc contenir des versions de pages qui ne correspondent plus au fichier principal sauvegardé. SQLite récupère alors les données de l’un à partir de l’autre, et le résultat est incorrect. Vous ne le découvrez que beaucoup plus tard :

Error: database disk image is malformed

.backup évite ce problème, car il utilise l’Online Backup API de SQLite. SQLite documente cette API comme la méthode à utiliser pour copier une base de données susceptible d’être utilisée activement. Elle lit les pages sous verrou de lecture et recommence si un writer modifie le fichier pendant l’opération. Le fichier écrit sur le disque correspond ainsi à un instant cohérent.

Effectuer la copie de la base de données avec 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;"

La dernière commande affiche ok sur sa propre ligne. Tout autre résultat signifie que la copie n’est pas utilisable. Ne la conservez pas et ne supprimez pas la précédente. Toute la séquence s’exécute sur un serveur actif. Aucun utilisateur n’est déconnecté et aucun conteneur ne redémarre.

L’outil sqlite3 n’est pas présent dans le conteneur Vaultwarden. L’image est basée sur debian:trixie-slim avec ca-certificates, curl, libmariadb3, libpq5 et openssl. Par conséquent, docker exec vaultwarden sqlite3 ... échoue avec :

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

Exécutez-le sur l’hôte, sur le chemin monté, comme le font les commandes précédentes. Si les données se trouvent dans un volume nommé, docker volume inspect <name> affiche le chemin sur l’hôte sous /var/lib/docker/volumes/.

Vaultwarden fournit également sa propre commande de sauvegarde depuis la version 1.32.1. Sur votre serveur :

docker exec -it vaultwarden /vaultwarden backup

Cette commande exécute VACUUM INTO et écrit db_YYYYMMDD_HHMMSS.sqlite3 dans le dossier de données. Deux conséquences sont à retenir. La copie est créée à côté de l’original, sur le même disque. Il s’agit donc d’une étape de préparation, et pas encore d’une sauvegarde. De plus, cette commande ne fonctionne qu’avec SQLite : avec MariaDB ou PostgreSQL, elle s’arrête avec The database type is not SQLite. Backups only works for SQLite databases.

Les fichiers que l’on oublie

attachments/ contient le texte chiffré sous des noms opaques. La ligne de base de données correspondant à chaque pièce jointe contient son nom de fichier chiffré ainsi que le matériel cryptographique dont le client a besoin pour déchiffrer le fichier. Sans la base de données, les pièces jointes sont illisibles. Sans les pièces jointes, les éléments affichés aux utilisateurs échouent lors du téléchargement. Sauvegardez les deux au cours de la même exécution.

config.json contient tout ce que vous avez enregistré depuis la page d’administration. Ses valeurs sont prioritaires sur les variables d’environnement correspondantes. Cela peut poser problème dans les deux sens : la restauration d’un ancien fichier config.json remplace discrètement les paramètres de votre fichier compose, et le fichier lui-même est sensible, car il peut contenir votre mot de passe SMTP et votre jeton d’administration. Stockez ce jeton sous forme de chaîne PHC Argon2id (password hashing competition), et non en texte brut. docker run --rm -it vaultwarden/server /vaultwarden hash en génère une pour vous.

rsa_key.pem signe les JSON Web Tokens (JWT) qui maintiennent les clients connectés. Si le fichier est absent au démarrage, Vaultwarden génère une nouvelle clé. Tous les jetons signés avec l’ancienne clé cessent alors d’être validés et tous les clients sont déconnectés. Le contenu du coffre reste intact, car il est chiffré avec des clés dérivées du mot de passe principal. La restauration du fichier de clé évite cette déconnexion générale.

sends/ contient les fichiers associés aux liens Send. Leur absence interrompt ces téléchargements, sans autre conséquence.

Mettez tout dans un seul 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"

Enregistrez-le sous /usr/local/sbin/vw-backup.sh, rendez-le exécutable avec chmod 700, puis exécutez-le en tant que root. La ligne test effectue un véritable contrôle : sqlite3 renvoie 0 même lorsque PRAGMA integrity_check signale une corruption. Comparer la sortie à ok transforme donc une copie incorrecte en échec du script. set -euo pipefail arrête ensuite toute l’exécution, au lieu de laisser tar créer une archive valide en apparence autour d’une base de données endommagée.

La commande finale tar -tzf affiche ce qui a réellement été capturé. Lisez sa sortie la première fois. Vérifiez la présence de ./db.sqlite3, ./rsa_key.pem, ./config.json et ./attachments/, ainsi que l’absence de ./db.sqlite3-wal. Exécutez le script chaque nuit avec un service et un timer systemd plutôt qu’avec cron si vous voulez une sortie journalctl et une unité qui signale les échecs.

Vérifier la sauvegarde en la restaurant dans un répertoire de test

Une sauvegarde qui n’a pas été testée reste une hypothèse. La restauration dans un répertoire de test prend une minute et ne modifie rien dans l’environnement actif.

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

Quatre résultats sont importants. integrity_check affiche ok. Le nombre d’utilisateurs correspond au nombre de comptes que vous connaissez. Le nombre de chiffrements est proche de la valeur active affichée par sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 "select count(*) from ciphers;", et il n’est jamais nul sur un vault utilisé. Le répertoire des pièces jointes a une taille approximativement conforme à vos attentes. Vous pouvez ignorer ce contrôle si personne n’envoie de pièces jointes. Exécutez ensuite sudo rm -rf /tmp/vw-check, car ce répertoire contient désormais une deuxième copie de tous les fichiers.

Lorsque vous restaurez un dossier de données copié manuellement, appliquez toujours cette règle : supprimez db.sqlite3-wal et db.sqlite3-shm avant de démarrer le serveur. Sinon, SQLite tentera de récupérer la base de données restaurée avec un journal appartenant à une autre copie. Cela corrompt une base de données qui était intacte à l’origine. Les archives produites par le script ci-dessus ne contiennent jamais ces fichiers, car .backup écrit une base de données complète.

Restaurer sur le serveur

Exécutez ces commandes sur votre propre serveur, avec le conteneur arrêté. Vaultwarden ne doit pas écrire pendant que le dossier de données est modifié sous-jacent.

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

Le chown doit indiquer l’utilisateur sous lequel le conteneur s’exécute. L’image standard s’exécute avec root ; root:root convient donc, sauf si vous avez défini user: dans votre fichier Compose. Dans ce cas, utilisez cet uid et ce gid. Si le serveur ne peut pas écrire dans le dossier de données, la page de connexion échoue à chaque requête. Les journaux l’indiquent.

Un démarrage correct se termine par la ligne Rocket :

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

Connectez-vous ensuite depuis un navigateur, ouvrez un élément et téléchargez une pièce jointe. Si la connexion fonctionne mais que le téléchargement des pièces jointes échoue, cela signifie que l’archive contient la base de données, mais pas attachments/. Conservez data.old.* jusqu’à ce que toutes ces vérifications soient terminées, puis supprimez-le. Pour revenir en arrière, effectuez les mêmes trois étapes en inversant les répertoires.

Si vos chemins diffèrent de ceux utilisés ici, le guide d’installation de Vaultwarden sur un VPS présente le fichier Compose attendu par ces commandes.

Où ne pas stocker la sauvegarde

  • Pas sur le même disque que le dossier de données. Une panne de volume supprime les deux copies, tout comme un rm -rf sur le mauvais chemin.
  • Pas sur le même serveur, même sur un second volume. Un attaquant qui obtient root accède à vos sauvegardes dans la même session.
  • Pas dans un stockage objet sans chiffrement, car l’archive contient des adresses e-mail, des indices de mot de passe, des codes de récupération et du texte chiffré de coffre-fort qui peuvent être attaqués hors ligne.
  • Ne vous limitez pas aux snapshots de votre fournisseur. Ils permettent une restauration rapide, ce qui est utile, mais ils résident dans le même compte que le serveur. Un problème de compte les affecte donc également.

C’est là qu’une copie hors site avec restic est utile, car un dépôt restic est chiffré sur la machine avant tout téléversement. Sur votre serveur :

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

Indiquez à restic le répertoire d’archive, et non le dossier de données actif, afin qu’il téléverse la copie cohérente que vous avez déjà vérifiée. Conservez le mot de passe du dépôt ailleurs que sur le serveur qu’il protège : si vous perdez ce mot de passe, les snapshots sont illisibles, par conception. Lorsque le stockage le permet, donnez au serveur des identifiants autorisés à écrire, mais pas à supprimer. Ainsi, une compromission du serveur ne peut pas effacer son propre historique. Configurer les sauvegardes restic sur un VPS présente en détail le dépôt et la planification. restic comparé à BorgBackup explique le choix si vous ne l’avez pas encore fait.

Tester la restauration selon un calendrier

Choisissez un jour par mois. Récupérez le snapshot le plus récent dans un répertoire de travail avec restic restore latest --tag vaultwarden --target /tmp/vw-check, exécutez le même PRAGMA integrity_check, effectuez les mêmes comptages de lignes, puis notez la date et les résultats. Une sauvegarde qui n’a été restaurée par personne depuis six mois est une sauvegarde dont l’état est inconnu. Vous découvrirez son état pendant une panne, au pire moment possible.

Une fois par an, effectuez le test complet. Démarrez un second conteneur Vaultwarden sur un port libre avec le répertoire de données restauré, puis connectez-vous avec un compte réel. Cela vérifie le fonctionnement de bout en bout avec le mot de passe maître, ce qu’aucun comptage de lignes ne peut faire. restic check --read-data-subset=10% selon le même calendrier vérifie que les données stockées sont lisibles et pas seulement répertoriées.

FAQ

Puis-je copier db.sqlite3 avec cp pendant que Vaultwarden fonctionne ?

Non. Vaultwarden utilise SQLite en mode WAL. Les écritures récentes se trouvent donc dans db.sqlite3-wal et ne sont pas encore dans db.sqlite3. Un cp du fichier principal seul les perd silencieusement. Copier les deux fichiers séparément peut aussi produire une paire incohérente, qui se manifestera plus tard par Error: database disk image is malformed. Utilisez plutôt sqlite3 /path/db.sqlite3 ".backup '/path/out.sqlite3'". Cette commande utilise l’Online Backup API de SQLite et produit un fichier cohérent pendant que le serveur continue de répondre aux requêtes.

Dois-je arrêter le conteneur Vaultwarden pour effectuer une sauvegarde ?

Non, et c’est précisément l’objectif de .backup. La copie de la base de données est sûre sur un serveur en fonctionnement. Les pièces jointes et les fichiers Send sont écrits lorsqu’un utilisateur en ajoute un. Un fichier ajouté entre la copie de la base de données et le tar peut donc manquer dans l’archive de cette nuit, ce qui vous fait perdre au maximum une pièce jointe. Si quelques secondes d’interruption ne vous gênent pas, exécuter docker compose stop avant le script et docker compose start après celui-ci élimine même ce risque.

Que se passe-t-il si je restaure la sauvegarde sans les fichiers rsa_key ?

Vaultwarden génère une nouvelle clé au démarrage. Cette clé signe les JSON web tokens (JWT) qui maintiennent les sessions actives. Tous les tokens existants cessent donc d’être valides, et tous les clients sont déconnectés. Ils doivent ensuite s’authentifier de nouveau. Le contenu du coffre reste intact, car il est chiffré avec des clés dérivées du mot de passe maître de chaque utilisateur, et non avec la clé RSA. Restaurez rsa_key.pem avec le reste du dossier de données pour que personne ne remarque la restauration.

L’archive de sauvegarde peut-elle être envoyée telle quelle dans un object storage ?

Non. Les noms des éléments, les mots de passe et les notes sont chiffrés. En revanche, les adresses e-mail, les noms de compte, les indices de mot de passe et les codes de récupération à deux facteurs sont stockés en clair dans la base de données. Un attaquant hors ligne peut aussi tenter de casser le chiffrement à son propre rythme. Chiffrez l’archive avant qu’elle ne quitte la machine. Un dépôt restic s’en charge pour vous, et gpg --symmetric --cipher-algo AES256 vw-20260805-030000.tar.gz produit un fichier chiffré unique que vous pouvez transmettre à n’importe quel stockage.

Comment sauvegarder Vaultwarden avec PostgreSQL ou MariaDB ?

Les étapes prévues pour SQLite ne s’appliquent pas. La commande intégrée refuse également l’opération avec The database type is not SQLite. Backups only works for SQLite databases. Exportez la base de données avec son outil natif, pg_dump ou mysqldump, puis appliquez toutes les autres règles. Le dump doit se trouver dans une archive avec attachments/, sends/, config.json et les fichiers rsa_key. Prenez tous ces éléments lors de la même exécution, chiffrez l’archive et stockez-la ailleurs que sur le serveur qui l’a créée.