Vaultwarden mit Docker – Schritt für Schritt
Deine Compose-Datei ist eine gute Grundlage. Ein paar Dinge darin machen allerdings Probleme. Die habe ich in der Anleitung korrigiert und unten am Ende erklärt.
1. Voraussetzungen
- Docker und das Docker-Compose-Plugin sind installiert (
docker compose version). - Es gibt einen DNS-Eintrag, z. B.
bitwarden.domain.com, der auf deinen Reverse Proxy zeigt. - Das externe Netz
docknetexistiert. Prüfen kannst du das mitdocker network ls. Falls es fehlt:
bash
docker network create --subnet 172.16.0.0/24 docknet
2. Verzeichnis anlegen
bash
mkdir -p /opt/vaultwarden && cd /opt/vaultwarden
3. Admin-Token als Hash erzeugen
bash
docker run --rm -it vaultwarden/server:latest /vaultwarden hash
Gib zweimal ein starkes Passwort ein und kopiere den Hash, der mit $argon2id$… beginnt. Mit diesem Passwort (nicht mit dem Hash) meldest du dich später unter /admin an.
4. .env mit den Geheimnissen anlegen
So stehen keine Passwörter direkt in der Compose-Datei.
bash
nano .env
env
# Einfache Anführungszeichen sind wichtig, damit die $-Zeichen im Hash nicht ersetzt werden ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$...' DB_NAME=vaultdb DB_USER=vaultuser DB_PASSWORD=EinLangesPasswortOhneSonderzeichen DB_ROOT_PASSWORD=NochEinLangesPasswort SMTP_PASSWORD='dein-smtp-passwort'
bash
chmod 600 .env
Tipp: Nimm für
DB_PASSWORDnur Buchstaben und Zahlen. Es landet in einer URL, und Zeichen wie@ : / # ?müssten dort sonst kodiert werden.
5. docker-compose.yml
yaml
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: bitwarden_web
restart: always
depends_on:
- dbvault8
environment:
DOMAIN: "https://bitwarden.domain.com"
ADMIN_TOKEN: ${ADMIN_TOKEN}
DATABASE_URL: "mysql://${DB_USER}:${DB_PASSWORD}@172.16.0.53:3306/${DB_NAME}"
TZ: "Europe/Vienna"
LOG_LEVEL: "info"
SIGNUPS_ALLOWED: "false"
SIGNUPS_VERIFY: "true"
INVITATIONS_ALLOWED: "false"
SHOW_PASSWORD_HINT: "false"
WEB_VAULT_ENABLED: "true"
SMTP_HOST: "mail.your-server.de"
SMTP_PORT: "587"
SMTP_SECURITY: "starttls"
SMTP_FROM: "no-reply@domain.com"
SMTP_FROM_NAME: "Bitwarden"
SMTP_USERNAME: "no-reply@domain.com"
SMTP_PASSWORD: ${SMTP_PASSWORD}
volumes:
- ./data:/data
networks:
docknet:
ipv4_address: 172.16.0.3
dbvault8:
image: mysql:8.4
container_name: bitwarden_db_8
restart: always
environment:
MYSQL_DATABASE: ${DB_NAME}
MYSQL_USER: ${DB_USER}
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
volumes:
- ./db-data_8:/var/lib/mysql
networks:
docknet:
ipv4_address: 172.16.0.53
networks:
docknet:
external: true6. Starten und Logs prüfen
bash
docker compose config # zeigt, ob die Variablen korrekt eingesetzt werden docker compose up -d docker compose logs -f vaultwarden
Beim ersten Start kann Vaultwarden ein paar Mal melden, dass die Datenbank nicht erreichbar ist, solange MySQL noch initialisiert. Das ist normal, weil es automatisch neu versucht. Fertig ist es, wenn Rocket has launched im Log steht.
7. Reverse Proxy (Nginx Proxy Manager)
Lege einen neuen Proxy Host an:
- Domain:
bitwarden.domain.com - Scheme / Forward IP / Port:
http·172.16.0.3·80 - Websockets Support: an (wird für die Live-Synchronisation gebraucht)
- Block Common Exploits: an
- SSL-Tab: Let’s-Encrypt-Zertifikat, Force SSL, HTTP/2 und HSTS aktivieren
Ports musst du am Container keine freigeben, weil NPM ihn direkt über docknet erreicht.
8. Ersten Benutzer anlegen
Da die Registrierung gesperrt ist, hast du zwei Möglichkeiten:
- Über das Admin-Panel: Öffne
https://bitwarden.domain.com/admin, melde dich mit dem Passwort aus Schritt 3 an und lade dich unter Users → Invite User selbst ein. Dafür muss SMTP funktionieren. - Ohne SMTP: Setze kurzzeitig
SIGNUPS_ALLOWED: "true"und starte mitdocker compose up -dneu. Dann registrierst du dich, stellst den Wert wieder auf"false"und startest erneut.
9. SMTP testen
Im Admin-Panel findest du unter SMTP Email Settings den Punkt Send test email.
⚠️ Wichtig: Was du im Admin-Panel speicherst, landet in
data/config.jsonund überschreibt von da an die Werte aus der Compose-Datei. Wenn eine Änderung an der Compose-Datei scheinbar nichts bewirkt, liegt es fast immer daran.
10. Backup
bash
# Datenbank sichern docker exec bitwarden_db_8 sh -c 'mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" vaultdb' > vaultdb_$(date +%F).sql # data-Ordner sichern (Anhänge, Sends, RSA-Schlüssel, config.json) tar czf vw-data_$(date +%F).tar.gz data/
Die Dateien data/rsa_key* gehören unbedingt ins Backup. Fehlen sie, sind nach einer Wiederherstellung alle Sitzungen ungültig.
11. Updates
bash
docker compose pull && docker compose up -d && docker image prune -f
Was ich an deiner Vorlage geändert habe
| Original | Problem | Lösung |
|---|---|---|
ports: ":80", ":443" | Damit werden zufällige Host-Ports veröffentlicht. Vaultwarden selbst spricht außerdem nur HTTP auf Port 80. | Ports entfernt, NPM regelt den Zugriff über docknet |
ADMIN_TOKEN="" | In der Listen-Schreibweise werden Anführungszeichen Teil des Werts. Der Token ist dann buchstäblich "" und das Admin-Panel ist mit "" offen. | Argon2-Hash aus der .env |
SMTP_FROM_NAME="bitwarden" | Gleiches Problem: Die Anführungszeichen landen im Absendernamen. | Map-Schreibweise verwendet |
DOMAIN=[https://…](…) | Das ist durch Kopieren als Markdown-Link kaputtgegangen. | https://bitwarden.domain.com |
WEBSOCKET_ENABLED | Seit Version 1.29 veraltet, Websockets laufen jetzt direkt über Port 80. | entfernt |
PUID / PGID | Diese Variablen nutzen nur Images von linuxserver.io, Vaultwarden ignoriert sie. | entfernt (bei Bedarf stattdessen user: "1000:1000") |
kein depends_on | Vaultwarden kann vor der Datenbank starten. | depends_on ergänzt |
mysql:8 | Ein schwammiger Tag, der sich bei einem pull unbemerkt ändern kann. | auf mysql:8.4 (LTS) festgelegt |
| Passwörter in der YAML-Datei | Sie landen leicht in Backups oder Git. | in die .env ausgelagert |
Admin-Token für Vaultwarden erstellen
Vaultwarden erwartet als ADMIN_TOKEN keinen Klartext, sondern einen Argon2-Hash deines Admin-Passworts. Beim Anmelden unter /admin tippst du das Passwort ein, und Vaultwarden vergleicht es mit dem Hash.
Schritt 1: Hash erzeugen
Variante A: Container läuft noch nicht
bash
docker run --rm -it vaultwarden/server:latest /vaultwarden hash
Variante B: Container läuft bereits
bash
docker exec -it bitwarden_web /vaultwarden hash
Du wirst zweimal nach dem Passwort gefragt:
Password: Confirm Password: ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$Zk9xT2...$q8Hk3...' Generation of the Argon2id PHC string took: 512ms
Kopiere die komplette Zeile mit ADMIN_TOKEN='…'.
Standardmäßig nutzt der Befehl die Bitwarden-Voreinstellung. Mit /vaultwarden hash --preset owasp bekommst du die etwas strengeren OWASP-Parameter. Beides ist sicher.
Schritt 2: Hash eintragen, auf die $-Zeichen achten
Im Hash stehen mehrere $-Zeichen. Docker Compose hält sie für Variablen und würde den Hash kaputt machen. Je nachdem, wo du ihn einträgst, gehst du anders vor:
In der .env (empfohlen): Übernimm die Zeile genau so, wie sie ausgegeben wurde, mit einfachen Anführungszeichen:
env
ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$Zk9xT2...$q8Hk3...'
In der docker-compose.yml steht dann:
yaml
ADMIN_TOKEN: ${ADMIN_TOKEN}Direkt in der docker-compose.yml: Hier musst du jedes $ verdoppeln:
yaml
ADMIN_TOKEN: "$$argon2id$$v=19$$m=65540,t=3,p=4$$Zk9xT2...$$q8Hk3..."Schritt 3: Prüfen und neu starten
bash
docker compose config | grep ADMIN_TOKEN # der Hash muss vollständig mit allen $ erscheinen docker compose up -d docker compose logs vaultwarden | grep -i admin
Erscheint im Log keine Warnung wie You are using a plain text ADMIN_TOKEN, hat es geklappt. Dann meldest du dich unter https://bitwarden.domain.com/admin mit dem Passwort an, nicht mit dem Hash.
Typische Stolperfallen
- Login klappt nicht, obwohl der Hash stimmt: Wurde im Admin-Panel schon einmal gespeichert, steht in
data/config.jsonein eigeneradmin_token. Der hat Vorrang vor der Compose-Datei. Ändere ihn entweder im Admin-Panel unter General Settings → Admin token oder entferne die Zeile ausconfig.jsonund starte den Container neu. - Hash wirkt abgeschnitten: Die
$-Zeichen wurden nicht maskiert (siehe Schritt 2).docker compose configzeigt dir das sofort. - Admin-Panel abschalten: Lässt du
ADMIN_TOKENganz weg, ist/admindeaktiviert. Das ist sinnvoll, wenn alles eingerichtet ist und du das Panel nicht mehr brauchst.
