Auto-héberger Vaultwarden, le gestionnaire de mots de passe compatible Bitwarden
Suivez ça sur votre propre serveur. Commencer gratuitement, sans carte
Les plans hébergés de Bitwarden, c’est bien jusqu’à ce que vous préfériez ne pas garder les mots de passe de votre équipe sur le serveur de quelqu’un d’autre, ou que vous vouliez simplement un coffre qui répond sur votre propre domaine. Vaultwarden est une réimplémentation en Rust, construite de zéro, de l’API du serveur Bitwarden, compatible avec les vraies extensions de navigateur, applications mobiles et le CLI Bitwarden, et assez légère pour tourner confortablement sur le serveur le moins cher que vend un fournisseur cloud. À la fin de ce guide, il tournera derrière HTTPS avec la synchronisation en direct fonctionnelle, un jeton admin qui ne traîne plus en texte clair, les inscriptions verrouillées à votre seule équipe, et une sauvegarde que vous pouvez réellement restaurer.
Ce qu’il vous faut
- Un serveur avec Docker et le plugin Docker Compose. Un Hetzner CX23 (2 vCPU, 4 Go de RAM, 40 Go NVMe, 5,99 € par mois hors TVA) est largement suffisant pour le coffre d’un foyer ou d’une petite équipe.
- Un domaine ou sous-domaine que vous contrôlez, pointé vers l’adresse IP du serveur (un enregistrement A du type
vault.votredomaine.com). - N’importe quel client compatible Bitwarden : l’extension de navigateur officielle, l’application de bureau, l’application mobile ou le CLI fonctionnent tous sans modification contre Vaultwarden.
- Un accès SSH au serveur.
Étape 1 : générer un jeton admin haché, pas en texte clair
La page /admin de Vaultwarden, où vous gérez les utilisateurs, les organisations et les paramètres du serveur, est protégée par une seule valeur ADMIN_TOKEN. Une chaîne en texte clair fonctionne encore, mais la documentation du projet elle-même signale que c’est non sécurisé, puisque quiconque peut lire vos variables d’environnement ou votre fichier compose obtient le jeton tel quel. La recommandation actuelle est de le stocker plutôt comme un hash Argon2id, en utilisant la commande de hachage intégrée de Vaultwarden :
docker run --rm -it vaultwarden/server:1.37.4 /vaultwarden hash
Ça demande un mot de passe et affiche un hash au format PHC qui ressemble à $argon2id$v=19$m=19456,t=2,p=1$<sel>$<hash> (la chaîne ci-dessus est illustrative, la vôtre aura un sel et un hash différents). Cette chaîne entière est ce que vous définissez comme ADMIN_TOKEN ; Vaultwarden hache les tentatives entrantes et compare, donc le vrai jeton vit dans votre tête, pas dans un fichier de config que n’importe qui peut grep.
Placez-le dans un fichier .env à côté de votre docker-compose.yml, non versionné :
ADMIN_TOKEN='$argon2id$v=19$m=19456,t=2,p=1$c29tZXNhbHR2YWx1ZQ$c29tZWhhc2h2YWx1ZQ'
Un fichier .env n’a besoin d’aucun échappement des caractères $. Si vous collez plutôt le hash directement dans le bloc environment: de docker-compose.yml, chaque $ doit être doublé en $$ ou Compose essaie de l’interpréter comme une substitution de variable, ce qui est une raison de plus pour laquelle le fichier .env est le chemin le plus simple.
Étape 2 : écrire le fichier Docker Compose
services:
vaultwarden:
image: vaultwarden/server:1.37.4
restart: unless-stopped
environment:
DOMAIN: "https://vault.votredomaine.com"
SIGNUPS_ALLOWED: "false"
ADMIN_TOKEN: "${ADMIN_TOKEN}"
volumes:
- ./vaultwarden-data:/data
expose:
- "80"
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy-data:/data
- caddy-config:/config
depends_on:
- vaultwarden
volumes:
caddy-data:
caddy-config:
1.37.4 est le tag de version stable actuel sur Docker Hub au moment de l’écriture ; épinglez une vraie version plutôt que latest pour qu’une mise à jour soit quelque chose que vous exécutez délibérément. DOMAIN doit être l’URL HTTPS exacte à laquelle les clients joindront le serveur, Vaultwarden l’utilise pour construire les liens dans les courriels et pour valider les origines WebAuthn et passkey, donc une incohérence ici est un vrai bug fonctionnel, pas juste cosmétique. SIGNUPS_ALLOWED démarre à true par défaut sur une installation Vaultwarden neuve, ce qui veut dire que quiconque trouve l’URL peut créer un compte ; mettez-le à "false" une fois que vos propres comptes existent, ce qui est l’étape 5 ci-dessous. Le répertoire de données est un bind mount simple plutôt qu’un volume nommé, précisément pour qu’une sauvegarde soit juste la copie d’un dossier, voir l’étape 6.
Étape 3 : pointer le domaine vers le serveur et démarrer la pile
Créez un enregistrement A pour votre sous-domaine pointant vers l’IP publique du serveur, puis démarrez les conteneurs :
docker compose up -d
Caddy demande automatiquement un certificat à Let’s Encrypt la première fois qu’il voit du trafic pour le domaine dans votre Caddyfile (étape 4), ce qui nécessite que l’enregistrement DNS résolve déjà correctement et que les ports 80 et 443 soient atteignables depuis Internet, pas seulement depuis votre propre machine.
Étape 4 : ajouter le proxy inverse Caddy
vault.votredomaine.com {
reverse_proxy vaultwarden:80
}
Cette seule ligne reverse_proxy est aussi toute la configuration WebSocket. Vaultwarden avait besoin autrefois d’un port séparé (3012) et d’un drapeau WEBSOCKET_ENABLED pour les notifications de synchronisation en direct qui poussent instantanément un élément nouveau ou modifié vers vos autres clients ouverts ; depuis la version 1.29.0, ces notifications tournent sur le même port que le reste de l’application, et la directive reverse_proxy de Caddy transmet déjà les en-têtes Upgrade et Connection dont une poignée de main WebSocket a besoin, sans configuration supplémentaire. Si vous adaptez ceci pour nginx à la place, c’est la seule paire d’en-têtes que vous devriez ajouter à la main, que Caddy vous donne gratuitement.
Étape 5 : créez votre compte, puis verrouillez la porte
Visitez https://vault.votredomaine.com, inscrivez le premier compte via le formulaire d’inscription normal, et confirmez que vous pouvez vous connecter et ajouter un élément de test. Une fois que tous les comptes dont votre équipe a besoin existent, coupez les inscriptions :
environment:
SIGNUPS_ALLOWED: "false"
Relancez docker compose up -d pour appliquer. À partir de là, les nouveaux comptes sont créés depuis la page /admin en utilisant votre jeton haché en Argon2 de l’étape 1, avec des invitations envoyées par courriel si vous avez configuré le SMTP, ou en partageant un lien d’invitation manuellement sinon.
Étape 6 : sauvegarder le répertoire de données
Tout ce qui compte, les données du coffre chiffrées, les pièces jointes, et la config du serveur, vit sous ./vaultwarden-data. Une archive tar quotidienne vers un endroit hors du serveur suffit pour la plupart des configurations :
tar czf vaultwarden-backup-$(date +%F).tar.gz ./vaultwarden-data
Gardez une poignée de sauvegardes récentes et copiez-en au moins une ailleurs que sur le serveur lui-même, un second serveur, du stockage objet, n’importe où qui survit à la panne du disque original. Restaurer est la même opération à l’envers : arrêtez la pile, extrayez l’archive par-dessus ./vaultwarden-data, redémarrez la pile.
Étape 7 : mettre à jour sans perdre de données
docker compose pull vaultwarden
docker compose up -d vaultwarden
Vaultwarden exécute automatiquement au démarrage les migrations de schéma dont il a besoin contre la base SQLite (ou externe configurée) dans /data, donc une mise à jour normale consiste juste à récupérer la nouvelle image et à recréer ce seul conteneur. Lisez les notes de version pour la version vers laquelle vous sautez avant un saut de plusieurs versions, puisque le projet signale occasionnellement un changement cassant pour les auto-hébergeurs dans une version spécifique.
Le raccourci : faites-le avec Railyard
Railyard propose Vaultwarden comme modèle d’application en un clic (vaultwarden dans le catalogue de modèles), construit depuis l’image officielle vaultwarden/server avec un volume /data persistant déjà configuré et ADMIN_TOKEN défini comme le seul secret que vous remplissez vous-même. Déployez-le directement : app.railyard.run/deploy?template=vaultwarden.
Deux différences par rapport au chemin manuel valent la peine d’être connues : le modèle suit actuellement le tag flottant latest plutôt qu’une version épinglée, donc là où ce guide vous fait choisir et mettre à jour une version précise vous-même, Railyard suit en amont automatiquement. Et les inscriptions sont ouvertes par défaut ici aussi, la même étape SIGNUPS_ALLOWED=false de l’étape 5 s’applique encore une fois que les comptes de votre équipe existent, c’est juste une variable d’environnement sur la page Settings de l’application plutôt qu’une ligne dans docker-compose.yml. Voir Déployer avec un bouton pour le fonctionnement général des modèles en un clic.
Dépannage
Le HTTPS ne s’émet jamais, Caddy réessaie en boucle. Presque toujours un problème DNS : confirmez que vault.votredomaine.com résout déjà vers l’IP du serveur depuis l’extérieur de votre propre réseau (pas seulement depuis le serveur lui-même), et que rien en amont ne bloque les ports 80 ou 443 en entrée. Les logs de Caddy (docker compose logs caddy) affichent l’erreur ACME exacte si Let’s Encrypt a rejeté la demande.
La synchronisation en direct ne met pas à jour les autres appareils, mais le coffre lui-même fonctionne bien. Vérifiez que rien entre le client et Caddy ne retire les en-têtes de mise à niveau WebSocket, un CDN ou une couche de proxy supplémentaire devant Caddy est le coupable habituel, puisque Caddy lui-même gère ça correctement d’office comme couvert à l’étape 4.
docker compose up échoue avec un port déjà utilisé. Quelque chose d’autre sur le serveur est déjà lié au port 80 ou 443, souvent un ancien proxy inverse ou un serveur web existant. sudo ss -tlnp | grep ':80\|:443' montre ce qui occupe le port.
La page admin rejette un jeton dont vous êtes sûr qu’il est correct. Confirmez que vous collez la chaîne de hash PHC complète incluant le préfixe $argon2id$ comme ADMIN_TOKEN, pas seulement le mot de passe que vous avez haché. Si vous avez édité la valeur directement dans docker-compose.yml plutôt que dans un fichier .env, vérifiez que chaque $ est doublé en $$, un seul $ non échappé est avalé par la substitution de variable de Compose avant même que Vaultwarden ne le voie.
Les écritures dans /data échouent avec une erreur de permissions. Ça apparaît le plus souvent après la restauration d’une sauvegarde en tant que root alors que le conteneur tourne avec un utilisateur différent ; faites un chown -R du répertoire pour correspondre au UID que les logs du conteneur montrent, puis redémarrez.
FAQ
Vaultwarden est-il vraiment compatible avec les vraies applications Bitwarden ? Oui, il implémente la même API que parlent les applications officielles et les extensions de navigateur, donc vous les pointez vers l’URL de votre propre serveur au lieu de bitwarden.com et rien d’autre ne change côté client.
Puis-je utiliser une autre base de données que le fichier SQLite par défaut ? Vaultwarden supporte aussi MySQL et PostgreSQL, via une variable d’environnement DATABASE_URL, ce qui vaut la peine une fois que plus qu’une poignée de personnes dépendent du coffre et que vous voulez que les sauvegardes soient gérées par votre outillage de base de données existant plutôt qu’une archive tar.
Ai-je besoin du SMTP configuré pour inviter des gens ? Non, vous pouvez partager un lien d’invitation manuellement depuis la page /admin sans jamais configurer de courriel sortant, même si de vraies invitations par courriel sont plus accueillantes pour quiconque n’est pas déjà à l’aise de coller un lien brut dans un navigateur.
Qu’arrive-t-il si je perds le serveur sans sauvegarde ? Chaque élément du coffre disparaît, il n’y a aucun chemin de réinitialisation de mot de passe vers des données qui n’ont jamais été sauvegardées ailleurs, ce qui est toute la raison pour laquelle l’étape 6 n’est pas optionnelle en pratique même si Docker fonctionnera volontiers sans elle.
Est-ce sûr pour toute une entreprise, ou juste pour un usage personnel ? Les organisations, les collections partagées et les permissions de groupe sont toutes supportées, comme chez Bitwarden hébergé ; ce qui passe le moins bien à l’échelle à la main, c’est tout ce qui est dans l’étape 6 et l’étape 7, exactement la partie qu’une option gérée retire de vos épaules.
Une fois en marche, un coffre auto-hébergé disparaît surtout dans l’arrière-plan, le vrai travail continu est l’habitude de sauvegarde et de mise à jour des étapes 6 et 7, pas la configuration initiale.
Pourquoi les gens choisissent Railyard pour ça
- Votre propre compte infonuagique. Déployez sur DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, ou n'importe quel serveur Linux par SSH, facturé directement par votre fournisseur, sans majoration.
- Un prix fixe par équipe. L'offre gratuite est gratuite pour toujours : un serveur plus un serveur de base de données, trois applications, sans carte. Pro coûte 19 $ par mois pour jusqu'à cinq serveurs et un nombre illimité d'applications. Team coûte 49 $ par mois avec des serveurs illimités. Aucuns frais par application, par déploiement, par siège ou par gigaoctet.
- Des gabarits en un clic. Déployez des applications open source, comme des bases de données, des outils d'analyse et des outils de développement, sur votre propre serveur en un clic.
- Des bases de données gérées. Postgres, MySQL et Redis, avec des sauvegardes planifiées, vérifiées par restauration, et la récupération à un point dans le temps.
- Déploiement par push. Déployez depuis GitHub, GitLab ou la CLI, avec HTTPS automatique et des mises en production sans interruption.