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 docknet existiert. Prüfen kannst du das mit docker 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_PASSWORD nur 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: true

6. 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 mit docker compose up -d neu. 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.json und ü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

OriginalProblemLö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_ENABLEDSeit Version 1.29 veraltet, Websockets laufen jetzt direkt über Port 80.entfernt
PUID / PGIDDiese Variablen nutzen nur Images von linuxserver.io, Vaultwarden ignoriert sie.entfernt (bei Bedarf stattdessen user: "1000:1000")
kein depends_onVaultwarden kann vor der Datenbank starten.depends_on ergänzt
mysql:8Ein schwammiger Tag, der sich bei einem pull unbemerkt ändern kann.auf mysql:8.4 (LTS) festgelegt
Passwörter in der YAML-DateiSie 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.json ein eigener admin_token. Der hat Vorrang vor der Compose-Datei. Ändere ihn entweder im Admin-Panel unter General Settings → Admin token oder entferne die Zeile aus config.json und starte den Container neu.
  • Hash wirkt abgeschnitten: Die $-Zeichen wurden nicht maskiert (siehe Schritt 2). docker compose config zeigt dir das sofort.
  • Admin-Panel abschalten: Lässt du ADMIN_TOKEN ganz weg, ist /admin deaktiviert. Das ist sinnvoll, wenn alles eingerichtet ist und du das Panel nicht mehr brauchst.