Introduction
Bitwarden est le gestionnaire de mots de passe open-source leader, mais son instance cloud pose des questions de souveraineté des données. En 2026, Vaultwarden (fork léger et performant de Bitwarden) domine les déploiements self-hosted grâce à sa compatibilité API totale et sa consommation ressources minimale (SQLite natif, <100MB RAM). Ce tutoriel advanced vous guide pour un déploiement production-ready : Docker Compose, reverse proxy SSL avec Traefik, authentification LDAP/SAML, backups chiffrés et automatisations API.
Pourquoi c'est crucial ? 80% des breaches proviennent de mots de passe faibles (Verizon DBIR 2025). Self-hostez pour un contrôle total, audits conformes RGPD et coûts <10€/mois. On part des bases Docker vers des configs enterprise comme monitoring Prometheus et scaling horizontal. Résultat : une instance haute-dispo accessible via apps mobiles/desktop Bitwarden officielles. Temps estimé : 45min. (128 mots)
Prérequis
- Serveur Linux (Ubuntu 24.04+ ou VPS comme Hetzner/DigitalOcean, 2 vCPU/4GB RAM)
- Docker 27+ et Docker Compose 2.29+
- Domaine avec DNS A/AAAA pointant vers l'IP serveur
- Certificat SSL wildcard (Let's Encrypt via Traefik)
- Connaissances Docker, YAML et bash (niveau advanced)
- Ports 80/443 ouverts (firewall UFW/AWS SG)
1. Structure du projet et .env
#!/bin/bash
mkdir -p ~/bitwarden/{data,backups,logs}
cd ~/bitwarden
cat > .env << EOF
DOMAIN=bitwarden.votredomaine.com
EMAIL=admin@votredomaine.com
TZ=Europe/Paris
BITWARDEN_ADMIN_TOKEN=supersecretadmintoken1234567890abcdef
WEBSOCKET_ENABLED=true
SIGNUPS_ALLOWED=false
INVITATIONS_ALLOWED=false
DISABLE_USER_REGISTRATION=true
EOF
chmod 600 .envCe script initialise la arborescence, génère un .env sécurisé avec variables critiques (token admin généré via openssl rand -base64 48). DOMAIN pour Traefik/SSL, SIGNUPS_ALLOWED=false pour prod. Piège : token faible = risque takeover ; utilisez pwgen ou 1Password pour génération.
Configuration initiale
Exécutez bash setup.sh dans un répertoire dédié. Le .env centralise les secrets : ne commitez jamais en Git. ADMIN_TOKEN sert pour l'interface /admin post-déploiement. TZ évite les drifts logs. On passe au docker-compose pour orchestrer Vaultwarden + Traefik.
2. Docker Compose avec Vaultwarden + Traefik
version: '3.8'
services:
traefik:
image: traefik:v3.0
restart: unless-stopped
ports:
- '80:80'
- '443:443'
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/traefik.yml:ro
- ./acme.json:/acme.json
environment:
- TZ=${TZ}
networks:
- vaultwarden
vaultwarden:
image: vaultwarden/server:latest
restart: unless-stopped
volumes:
- ./data/:/data/
environment:
- DOMAIN=https://${DOMAIN}
- WEBSOCKET_ENABLED=${WEBSOCKET_ENABLED}
- SIGNUPS_ALLOWED=${SIGNUPS_ALLOWED}
- ADMIN_TOKEN=${BITWARDEN_ADMIN_TOKEN}
- TZ=${TZ}
labels:
- "traefik.enable=true"
- "traefik.http.routers.vaultwarden.rule=Host(`$${DOMAIN}`)"
- "traefik.http.routers.vaultwarden.tls.certresolver=letsencrypt"
- "traefik.http.routers.vaultwarden.entrypoints=websecure"
- "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
networks:
- vaultwarden
networks:
vaultwarden:
driver: bridgeCe compose.yml déploie Vaultwarden derrière Traefik pour SSL auto (Let's Encrypt). Labels Traefik routent HTTPS://DOMAIN vers port 80 interne. Volumes persistant data SQLite. Piège : sans WEBSOCKET_ENABLED, synchro temps réel (apps mobiles) échoue ; testez avec docker compose up -d.
3. Config Traefik statique
global:
checkNewVersion: false
sendAnonymousUsage: false
entryPoints:
web:
address: ':80'
http:
redirections:
entryPoint:
to: websecure
scheme: https
permanente: true
websecure:
address: ':443'
providers:
docker:
exposedByDefault: false
certificatesResolvers:
letsencrypt:
acme:
email: ${EMAIL}
storage: acme.json
httpChallenge:
entryPoint: webtraefik.yml configure entrypoints HTTP/HTTPS avec redirect auto et ACME Let's Encrypt. acme.json (chmod 600 pré-setup) stocke certs. Piège : sans exposedByDefault: false, tous conteneurs exposés = faille sécurité ; redémarrez Traefik après création acme.json vide.
Premier démarrage et admin
Créez touch acme.json && chmod 600 acme.json. Lancez docker compose up -d. Vérifiez logs : docker compose logs -f traefik. Accédez https://bitwarden.votredomaine.com. Créez compte admin via /admin avec token. Interface : activez 2FA TOTP/YubiKey.
4. Backup automatisé chiffré
#!/bin/bash
set -e
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR=~/bitwarden/backups
GPG_KEY=yourgpgkeyid
DATA_DIR=~/bitwarden/data
tar czf /tmp/vaultwarden-${DATE}.tar.gz -C ${DATA_DIR}/..
gpg --batch --yes --encrypt --recipient ${GPG_KEY} --output ${BACKUP_DIR}/vaultwarden-${DATE}.tar.gz.gpg /tmp/vaultwarden-${DATE}.tar.gz
rm /tmp/vaultwarden-${DATE}.tar.gz
find ${BACKUP_DIR} -type f -mtime +30 -delete
# Cron: 0 2 * * * /path/to/backup.shScript daily backup data/ chiffré GPG, rotation 30j. set -e stoppe sur erreur. Piège : sans GPG_KEY (gpg --gen-key pré-setup), backup plaintext = risque ; testez bash backup.sh et gpg --decrypt file.gpg.
Intégrations enterprise
LDAP/SAML : Ajoutez env LDAP_SERVER=ldap://dc.example.com dans .env, relancez. Scaling : Ajoutez Redis pour HA (REDIS_URL=redis://redis:6379). Vérifiez /admin → Settings.
5. Monitoring Prometheus + Grafana
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
ports:
- '9090:9090'
grafana:
image: grafana/grafana:latest
ports:
- '3000:3000'
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin123
vaultwarden-exporter:
image: ghcr.io/wystia/vaultwarden-exporter:latest
command: --vaultwarden-url=http://vaultwarden:80
ports:
- '8080:8080'Extension monitoring : exporter métriques Vaultwarden (logins, vaults). prometheus.yml à créer avec scrape vaultwarden-exporter. Piège : sans dashboard Grafana import 1860 (Bitwarden), métriques inutiles ; accédez Grafana:3000.
6. Script API : Lister vaults (Node.js)
import fetch from 'node-fetch';
const BW_URL = 'https://bitwarden.votredomaine.com';
const BW_EMAIL = 'admin@example.com';
const BW_PASSWORD = 'strongpassword123';
const BW_CLIENT_ID = 'yourclientid';
const BW_CLIENT_SECRET = 'yourclientsecret';
async function login() {
const response = await fetch(`${BW_URL}/identity/connect/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'password',
username: BW_EMAIL,
password: BW_PASSWORD,
scope: 'api',
client_id: BW_CLIENT_ID,
client_secret: BW_CLIENT_SECRET,
}),
});
const data = await response.json();
return data.access_token;
}
async function listVaults(token: string) {
const response = await fetch(`${BW_URL}/api/accounts`, {
headers: { Authorization: `Bearer ${token}` },
});
const accounts = await response.json();
console.log('Vaults:', accounts.data);
}
(async () => {
const token = await login();
await listVaults(token);
})();Exemple TS pour API Bitwarden : login OAuth, liste comptes/vaults. Créez app /admin → API → Client ID/Secret. npm i node-fetch. Piège : scope 'api' obligatoire ; token expire 5min, refresh via refresh_token.
Bonnes pratiques
- Secrets rotation : Changez ADMIN_TOKEN/PW mensuel via /admin → Rotate.
- HA setup : 3 nodes Vaultwarden + Postgres (env DATABASE_URL=postgres://...) + Redis.
- Audit logs : Activez LOG_FILE=/data/logs, ship vers ELK/Fluentd.
- Zero Trust : Fail2ban sur logs Traefik + WAF Cloudflare.
- MFA enforced : /admin → Policies → Require 2FA pour tous users.
Erreurs courantes à éviter
- Certs non persistant : Oubli acme.json = downtime SSL à chaque restart.
- SQLite en prod : >100 users → migrez Postgres (
docker exec vaultwarden sqlite3 /data/db.sqlite3 .dump | psql). - Websockets ignorés : Apps déconnectées ; vérifiez DOMAIN=https:// et port 3012 exposé si besoin.
- Pas de backups : Perte data irréversible ; testez restore weekly.
Pour aller plus loin
Explorez docs Vaultwarden, Bitwarden API. Intégrez avec Authentik pour SSO. Découvrez nos formations Learni sur DevSecOps pour masterclass self-hosting sécurisé.