homelab-blueprint/docker-compose-files/vaultwarden/README.md
Brian Pooe da5652aa10 feat(vaultwarden): add SSO/OIDC support with Zitadel documentation
- Add SSO environment variables to template.yaml (SSO_ENABLED, SSO_ONLY,
  SSO_AUTHORITY, SSO_CLIENT_ID, SSO_CLIENT_SECRET, SSO_SCOPES, SSO_PKCE,
  SSO_AUDIENCE_TRUSTED)
- Add SSO variables to .env.sample with Zitadel audience quirk explained
- Document full Zitadel setup in README: app creation, redirect URI, OIDC
  settings, and the audience fix (SSO_AUDIENCE_TRUSTED=^\d{18}$)
- Update .gitignore to track .env.sample files in docker-compose-files
2026-07-02 22:25:15 +02:00

5.4 KiB

Vaultwarden

Unofficial Bitwarden-compatible server written in Rust. Lightweight self-hosted password manager with full Bitwarden client support.

Persistent Data

All Vaultwarden state (vault database, attachments, config) is stored in the Docker-managed named volume vaultwarden_data.

Installation

  1. Create .env if it does not exist:

    cp -n .env.sample .env
    
  2. Set the required values in .env:

    TZ=Africa/Johannesburg
    VAULTWARDEN_DOMAIN=https://vault.home.example.com
    VAULTWARDEN_SIGNUPS_ALLOWED=false
    VAULTWARDEN_PORT=8800
    VAULTWARDEN_MEM_LIMIT=256m
    
    DOCKERLOGGING_MAXFILE=3
    DOCKERLOGGING_MAXSIZE=10m
    

    VAULTWARDEN_DOMAIN must match the URL clients use to reach the server. FIDO2/WebAuthn will not work without a correct domain.

  3. Generate an admin token (required to access /admin):

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

    Paste the resulting argon2 hash into VAULTWARDEN_ADMIN_TOKEN in .env. Leave it blank to disable the admin panel entirely.

  4. Render the template:

    ./substitute_env.sh docker-compose-files/vaultwarden/template.yaml docker-compose.vaultwarden.yml .env
    

    docker-compose.vaultwarden.yml contains the rendered admin token and is ignored by Git.

  5. Validate and start Vaultwarden:

    docker compose -f docker-compose.vaultwarden.yml config --quiet
    docker compose -f docker-compose.vaultwarden.yml up -d
    
  6. Verify the container and open http://<host-ip>:8800:

    docker compose -f docker-compose.vaultwarden.yml ps
    docker compose -f docker-compose.vaultwarden.yml logs --tail=100 vaultwarden
    

Reverse Proxy

Vaultwarden should be placed behind a TLS-terminating reverse proxy (Caddy). Add a Caddyfile entry:

vaultwarden.home.example.com {
    reverse_proxy localhost:8800
}

WebSocket notifications (/notifications/hub) are handled on the same HTTP port as of Vaultwarden 1.29 — no separate port or path rewrite is needed.

Apply Configuration Changes

After changing .env or template.yaml, render the Compose file and recreate the container:

./substitute_env.sh docker-compose-files/vaultwarden/template.yaml docker-compose.vaultwarden.yml .env
docker compose -f docker-compose.vaultwarden.yml config --quiet
docker compose -f docker-compose.vaultwarden.yml up -d

Optional: Mobile Push Notifications

Register at https://bitwarden.com/host/ to obtain an installation ID and key, then set in .env:

VAULTWARDEN_PUSH_ENABLED=true
VAULTWARDEN_PUSH_ID=<installation-id>
VAULTWARDEN_PUSH_KEY=<installation-key>

Optional: Email

Set the shared SMTP variables to enable email verification, 2FA codes, and emergency access notifications:

EMAIL_HOST=smtp.example.com
EMAIL_PORT=587
EMAIL_HOST_USER=user@example.com
EMAIL_HOST_PASSWORD=<password>
VAULTWARDEN_SMTP_FROM=vault@example.com
VAULTWARDEN_SMTP_SECURITY=starttls

SSO via OIDC (Zitadel)

Vaultwarden supports single sign-on through any OIDC provider. The steps below use Zitadel.

Zitadel application setup

  1. In the Zitadel console, create or select a Project.
  2. Inside that project add a new Application → type Web.
  3. Configure the OIDC settings:
    • Response Type: Code
    • Authentication Method: Basic
    • Grant Types: Authorization Code, Refresh Token
  4. Under Redirect Settings add the redirect URI:
    https://vaultwarden.home.example.com/identity/connect/oidc-signin
    
  5. Note the Client ID and generate a Client Secret.

Vaultwarden .env settings

VAULTWARDEN_SSO_ENABLED=true
VAULTWARDEN_SSO_ONLY=true          # set false to keep password login as fallback
VAULTWARDEN_SSO_AUTHORITY=https://zitadel.example.com
VAULTWARDEN_SSO_CLIENT_ID=<client-id>
VAULTWARDEN_SSO_CLIENT_SECRET=<client-secret>
VAULTWARDEN_SSO_SCOPES=openid email profile offline_access
VAULTWARDEN_SSO_PKCE=true
VAULTWARDEN_SSO_AUDIENCE_TRUSTED=^\d{18}$

Zitadel audience quirk

Zitadel includes the client IDs of all applications in the same project in the aud claim of the ID token, not just the one the user authenticated with. Vaultwarden rejects the token if any audience value is not trusted.

SSO_AUDIENCE_TRUSTED=^\d{18}$ trusts any 18-digit Zitadel ID, which covers all app client IDs that may appear in the audience. If your Zitadel instance uses a different ID length, adjust the digit count accordingly. You can also pin specific IDs: ^id1|id2|id3$.

The offline_access scope is required to obtain a refresh token so that Vaultwarden can extend sessions without re-authentication.

Organisation SSO identifier

When logging in with the Bitwarden client, enter your organisation's SSO identifier (configured in the Vaultwarden admin panel under /admin/organizations) when prompted.

Backup

Back up the vaultwarden_data named volume. It contains the SQLite database, attachments, and sends.

docker run --rm \
  -v vaultwarden_data:/data:ro \
  -v /opt/backups/vaultwarden:/backup \
  busybox tar czf /backup/vaultwarden-$(date +%Y%m%d).tar.gz -C /data .

Useful Commands

# Tail logs
docker compose -f docker-compose.vaultwarden.yml logs -f vaultwarden

# Open a shell inside the container
docker exec -it vaultwarden /bin/bash

# Check published port
docker compose -f docker-compose.vaultwarden.yml port vaultwarden 80