← Blog

Vaultwarden selbst hosten, den Bitwarden-kompatiblen Passwort-Manager

Railyard

9. Mai 2026 · 7 Min. Lesezeit

Folge dem auf deinem eigenen Server. Kostenlos starten, keine Karte nötig

Bitwardens gehostete Pläne sind in Ordnung, bis du die Passwörter deines Teams lieber nicht auf dem Server eines anderen behalten möchtest, oder du einfach einen Tresor willst, der unter deiner eigenen Domain erreichbar ist. Vaultwarden ist eine von Grund auf neu geschriebene Rust-Reimplementierung der Bitwarden-Server-API, kompatibel mit den echten Bitwarden-Browser-Erweiterungen, Mobile-Apps und der CLI, und leichtgewichtig genug, um problemlos auf dem günstigsten Server eines Cloud-Anbieters zu laufen. Am Ende dieser Anleitung läuft sie hinter HTTPS mit funktionierendem Live-Sync, einem Admin-Token, der nicht mehr im Klartext herumliegt, Registrierungen, die auf dein Team beschränkt sind, und einem Backup, aus dem du tatsächlich wiederherstellen kannst.

Was du brauchst

  • Einen Server mit Docker und dem Docker-Compose-Plugin. Ein Hetzner CX23 (2 vCPU, 4 GB RAM, 40 GB NVMe, 5,99 €/Monat exkl. MwSt.) reicht für den Tresor eines Haushalts oder kleinen Teams völlig aus.
  • Eine Domain oder Subdomain, die du kontrollierst, mit einem A-Record auf die IP-Adresse des Servers (z. B. vault.deinedomain.com).
  • Jeden Bitwarden-kompatiblen Client: die offizielle Browser-Erweiterung, die Desktop-App, die Mobile-App oder die CLI funktionieren alle unverändert mit Vaultwarden.
  • SSH-Zugang zum Server.

Schritt 1: Einen gehashten Admin-Token erzeugen, keinen im Klartext

Vaultwardens /admin-Seite, auf der du Nutzer, Organisationen und Server-Einstellungen verwaltest, wird durch einen einzigen ADMIN_TOKEN-Wert geschützt. Ein Klartext-String funktioniert noch, aber die Dokumentation des Projekts selbst weist darauf hin, dass das unsicher ist, da jeder, der deine Umgebungsvariablen oder die Compose-Datei lesen kann, den Token im Klartext erhält. Die aktuelle Empfehlung ist, ihn stattdessen als Argon2id-Hash zu speichern, mit Vaultwardens eigenem eingebauten Hashing-Befehl:

docker run --rm -it vaultwarden/server:1.37.4 /vaultwarden hash

Das fragt nach einem Passwort und gibt einen Hash im PHC-Format aus, der so aussieht wie $argon2id$v=19$m=19456,t=2,p=1$<salt>$<hash> (der String oben ist nur ein Beispiel, deiner hat einen anderen Salt und Hash). Dieser gesamte String ist das, was du als ADMIN_TOKEN setzt; Vaultwarden hasht eingehende Versuche und vergleicht sie, sodass der echte Token in deinem Kopf lebt, nicht in einer Konfigurationsdatei, die jeder grep-en kann.

Trage ihn in eine .env-Datei neben deiner docker-compose.yml ein, nicht in die Versionskontrolle committet:

ADMIN_TOKEN='$argon2id$v=19$m=19456,t=2,p=1$c29tZXNhbHR2YWx1ZQ$c29tZWhhc2h2YWx1ZQ'

Eine .env-Datei braucht kein Escaping der $-Zeichen. Wenn du den Hash stattdessen direkt in den environment:-Block der docker-compose.yml einfügst, muss jedes $ zu $$ verdoppelt werden, sonst versucht Compose, es als Variablensubstitution zu interpretieren, was ein weiterer Grund ist, warum die .env-Datei der einfachere Weg ist.

Schritt 2: Die Docker-Compose-Datei schreiben

services:
  vaultwarden:
    image: vaultwarden/server:1.37.4
    restart: unless-stopped
    environment:
      DOMAIN: "https://vault.yourdomain.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 ist der aktuelle Stable-Release-Tag auf Docker Hub zum Zeitpunkt der Erstellung; pinne eine echte Version statt latest, damit ein Upgrade etwas ist, das du bewusst ausführst. DOMAIN muss genau die HTTPS-URL sein, unter der Clients den Server erreichen, Vaultwarden nutzt sie, um Links in E-Mails zu bauen und um WebAuthn- und Passkey-Origins zu validieren, sodass ein Mismatch hier ein echter funktionaler Bug ist, nicht nur kosmetisch. SIGNUPS_ALLOWED startet standardmäßig auf true bei einer frischen Vaultwarden-Installation, das heißt, jeder, der die URL findet, kann ein Konto anlegen; setze es auf "false", sobald deine eigenen Konten existieren, das ist Schritt 5 unten. Das Datenverzeichnis ist ein einfacher Bind-Mount statt eines benannten Volumes, gerade damit ein Backup nur das Kopieren eines Ordners ist, siehe Schritt 6.

Schritt 3: Die Domain auf den Server zeigen lassen und den Stack starten

Erstelle einen A-Record für deine Subdomain, der auf die öffentliche IP des Servers zeigt, und starte dann die Container:

docker compose up -d

Caddy fordert beim ersten gesehenen Traffic für die Domain in deiner Caddyfile (Schritt 4) automatisch ein Zertifikat von Let’s Encrypt an, das setzt voraus, dass der DNS-Eintrag schon aufgelöst wird und die Ports 80 und 443 aus dem Internet erreichbar sind, nicht nur von deiner eigenen Maschine.

Schritt 4: Den Caddy-Reverse-Proxy hinzufügen

vault.yourdomain.com {
  reverse_proxy vaultwarden:80
}

Diese eine reverse_proxy-Zeile ist auch das gesamte WebSocket-Setup. Vaultwarden brauchte früher einen separaten Port (3012) und ein WEBSOCKET_ENABLED-Flag für die Live-Sync-Benachrichtigungen, die ein neues oder geändertes Element sofort an deine anderen offenen Clients pushen; seit Version 1.29.0 laufen diese Benachrichtigungen über denselben Port wie der Rest der App, und Caddys reverse_proxy-Direktive leitet die Upgrade- und Connection-Header, die ein WebSocket-Handshake braucht, bereits ohne zusätzliche Konfiguration weiter. Wenn du das stattdessen für nginx anpasst, ist das das eine Header-Paar, das du von Hand ergänzen müsstest, das Caddy dir umsonst gibt.

Schritt 5: Dein Konto anlegen, dann die Tür verschließen

Besuche https://vault.deinedomain.com, registriere das erste Konto über das normale Anmeldeformular, und bestätige, dass du dich einloggen und ein Testelement hinzufügen kannst. Sobald jedes Konto existiert, das dein Team braucht, schalte die Registrierung aus:

environment:
  SIGNUPS_ALLOWED: "false"

Führe docker compose up -d erneut aus, um das anzuwenden. Von hier an werden neue Konten über die /admin-Seite mit deinem Argon2-gehashten Token aus Schritt 1 angelegt, mit Einladungen per E-Mail, falls du SMTP konfiguriert hast, oder durch manuelles Teilen eines Einladungslinks, falls nicht.

Schritt 6: Das Datenverzeichnis sichern

Alles, was zählt, verschlüsselte Tresordaten, Anhänge und Server-Konfiguration, liegt unter ./vaultwarden-data. Ein tägliches Tarball an einen Ort außerhalb des Servers reicht für die meisten Setups:

tar czf vaultwarden-backup-$(date +%F).tar.gz ./vaultwarden-data

Behalte eine Handvoll aktueller Backups und kopiere mindestens eines davon an einen Ort außerhalb des Servers selbst, einen zweiten Server, Object Storage, irgendwohin, das einen Ausfall der Original-Disk überlebt. Das Wiederherstellen ist derselbe Vorgang in umgekehrter Richtung: Stack anhalten, das Tarball über ./vaultwarden-data zurück entpacken, Stack wieder starten.

Schritt 7: Upgrade ohne Datenverlust

docker compose pull vaultwarden
docker compose up -d vaultwarden

Vaultwarden führt beim Start automatisch alle nötigen Schema-Migrationen gegen die SQLite- (oder konfigurierte externe) Datenbank in /data aus, sodass ein normales Upgrade nur das Pullen des neuen Images und das Neuerstellen des einen Containers ist. Lies vor einem Sprung über mehrere Versionen die Release Notes der Zielversion, da das Projekt gelegentlich einen Breaking Change für Selbsthoster in einem bestimmten Release ausweist.

Die Abkürzung: mit Railyard

Railyard liefert Vaultwarden als Ein-Klick-Anwendungs-Template (vaultwarden im Template-Katalog), gebaut aus dem offiziellen vaultwarden/server-Image mit einem bereits verdrahteten persistenten /data-Volume und ADMIN_TOKEN als dem einen Secret, das du selbst ausfüllst. Direkt bereitstellen: app.railyard.run/deploy?template=vaultwarden.

Zwei Unterschiede zum manuellen Weg, die wichtig zu wissen sind: Das Template folgt derzeit dem schwimmenden latest-Tag statt einer gepinnten Version, wo diese Anleitung dich eine bestimmte Version wählen und selbst upgraden lässt, bewegt sich Railyard automatisch mit Upstream mit. Und Registrierungen sind hier standardmäßig auch offen, derselbe SIGNUPS_ALLOWED=false-Schritt aus Schritt 5 gilt weiterhin, sobald die Konten deines Teams existieren, es ist nur eine Umgebungsvariable auf der Settings-Seite der App statt einer Zeile in docker-compose.yml. Siehe Mit einem Button bereitstellen dafür, wie Ein-Klick-Templates im Allgemeinen funktionieren.

Troubleshooting

HTTPS wird nie ausgestellt, Caddy versucht es immer wieder. Fast immer ein DNS-Problem: bestätige, dass vault.deinedomain.com bereits von außerhalb deines eigenen Netzwerks auf die IP des Servers aufgelöst wird (nicht nur vom Server selbst), und dass nichts vorgeschaltet eingehenden Traffic auf 80 oder 443 blockiert. Caddys eigene Logs (docker compose logs caddy) geben den genauen ACME-Fehler aus, falls Let’s Encrypt die Anfrage abgelehnt hat.

Live-Sync aktualisiert andere Geräte nicht, aber der Tresor selbst funktioniert einwandfrei. Prüfe, ob zwischen Client und Caddy etwas WebSocket-Upgrade-Header entfernt, ein CDN oder eine zusätzliche Proxy-Schicht vor Caddy ist der übliche Übeltäter, da Caddy selbst das von Haus aus korrekt handhabt, wie in Schritt 4 beschrieben.

docker compose up schlägt mit einem belegten Port fehl. Etwas anderes auf dem Server ist bereits an 80 oder 443 gebunden, häufig ein vorheriger Reverse-Proxy oder ein bestehender Webserver. sudo ss -tlnp | grep ':80\|:443' zeigt, was den Port belegt.

Die Admin-Seite lehnt einen Token ab, von dem du sicher bist, dass er korrekt ist. Bestätige, dass du den vollständigen PHC-Hash-String inklusive dem $argon2id$-Präfix als ADMIN_TOKEN einfügst, nicht nur das Passwort, das du gehasht hast. Wenn du den Wert direkt in der docker-compose.yml statt in einer .env-Datei bearbeitet hast, prüfe, dass jedes $ zu $$ verdoppelt ist, ein einzelnes unescaptes $ wird von Composes eigener Variablensubstitution verschluckt, bevor Vaultwarden es überhaupt sieht.

Schreibzugriffe auf /data schlagen mit einem Permissions-Fehler fehl. Das tritt am häufigsten nach dem Wiederherstellen eines Backups als root auf, während der Container als anderer Nutzer läuft; chown -R das Verzeichnis auf die UID, mit der der Container laut seinen Logs läuft, dann neu starten.

FAQ

Ist Vaultwarden tatsächlich kompatibel mit den echten Bitwarden-Apps? Ja, es implementiert dieselbe API, die die offiziellen Apps und Browser-Erweiterungen sprechen, du zeigst sie also einfach auf die URL deines eigenen Servers statt auf bitwarden.com, sonst ändert sich clientseitig nichts.

Kann ich eine andere Datenbank als die Standard-SQLite-Datei nutzen? Vaultwarden unterstützt auch MySQL und PostgreSQL, über eine DATABASE_URL-Umgebungsvariable, was sich lohnt, sobald mehr als eine Handvoll Leute vom Tresor abhängen und du Backups lieber mit deinem bestehenden Datenbank-Tooling erledigen willst statt mit einem Tarball.

Brauche ich SMTP konfiguriert, um Leute einzuladen? Nein, du kannst einen Einladungslink manuell von der /admin-Seite teilen, ohne jemals ausgehende Mail einzurichten, echte E-Mail-Einladungen sind aber freundlicher für jeden, der sich nicht schon damit wohlfühlt, einen rohen Link in einen Browser zu pasten.

Was passiert, wenn ich den Server ohne Backup verliere? Jedes Tresor-Element ist weg, es gibt keinen Passwort-Reset-Weg zurück zu Daten, die nirgendwo sonst gesichert wurden, das ist der ganze Grund, warum Schritt 6 in der Praxis nicht optional ist, obwohl Docker problemlos ohne ihn läuft.

Ist das für ein ganzes Unternehmen sicher, oder nur für persönlichen Gebrauch? Organisationen, geteilte Collections und Gruppenberechtigungen werden alle unterstützt, genau wie bei gehostetem Bitwarden; das, was sich von Hand am wenigsten elegant skaliert, ist alles aus Schritt 6 und Schritt 7, genau der Teil, den dir eine verwaltete Option abnimmt.

Einmal läuft er, verschwindet ein selbst gehosteter Tresor meist im Hintergrund, die eigentliche fortlaufende Arbeit ist die Backup- und Upgrade-Routine aus Schritt 6 und Schritt 7, nicht das initiale Setup.

Warum Leute sich dafür Railyard entscheiden

  • Dein eigenes Cloud-Konto. Deploye auf DigitalOcean, Hetzner, Vultr, AWS, OVHcloud oder jedem Linux-Server per SSH, direkt von deinem Anbieter abgerechnet, ohne Aufschlag.
  • Ein fester Preis pro Team. Free ist für immer kostenlos: ein Server plus ein Datenbankserver, drei Apps, keine Karte. Pro kostet 19 $ im Monat für bis zu fünf Server und unbegrenzte Apps. Team kostet 49 $ im Monat mit unbegrenzten Servern. Keine Kosten pro App, pro Deployment, pro Sitz oder pro Gigabyte.
  • Templates mit einem Klick. Deploye Open-Source-Apps, wie Datenbanken, Analytics und Dev-Tools, mit einem Klick auf deinem eigenen Server.
  • Verwaltete Datenbanken. Postgres, MySQL und Redis mit geplanten, wiederherstellungsgeprüften Backups und Point-in-Time-Recovery.
  • Push-to-Deploy. Deploye von GitHub, GitLab oder der CLI, mit automatischem HTTPS und Releases ohne Ausfallzeit.

Vaultwarden mit einem Klick bereitstellen

Railyards Vaultwarden-Template richtet Image und Datenvolume für dich ein, du musst nur noch den Admin-Token eintragen.

Kostenloser Plan, keine Kreditkarte, und 14 Tage Pro für jedes neue Team. Ein fester Preis pro Team.