- Add compose stacks for apps already proxied by Caddy: bento-pdf,
it-tools, paperless-ngx, and beszel-hub (with Makefile targets)
- Remove the unused Vault stack and the drawio Caddyfile block
- Fix Caddyfile gramps upstream to port 80 to match the compose file
- Bring immich and gramps-web up to the shared template pattern
(no-new-privileges, log rotation, memory limits, healthchecks)
- Single-quote vaultwarden SSO_AUDIENCE_TRUSTED so regex values like
^\d{18}$ render as valid YAML
- Move root docs into kebab-case topic folders, rebuild the docs index
to cover every doc, and fix all broken links
|
||
|---|---|---|
| .. | ||
| .env.sample | ||
| README.md | ||
| template.yaml | ||
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
-
Create
.envif it does not exist:cp -n .env.sample .env -
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=10mVAULTWARDEN_DOMAINmust match the URL clients use to reach the server. FIDO2/WebAuthn will not work without a correct domain. -
Generate an admin token (required to access
/admin):docker run --rm -it vaultwarden/server /vaultwarden hashPaste the resulting argon2 hash into
VAULTWARDEN_ADMIN_TOKENin.env. Leave it blank to disable the admin panel entirely. -
Render the template:
./substitute_env.sh docker-compose-files/vaultwarden/template.yaml docker-compose.vaultwarden.yml .envdocker-compose.vaultwarden.ymlcontains the rendered admin token and is ignored by Git. -
Validate and start Vaultwarden:
docker compose -f docker-compose.vaultwarden.yml config --quiet docker compose -f docker-compose.vaultwarden.yml up -d -
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
- In the Zitadel console, create or select a Project.
- Inside that project add a new Application → type Web.
- Configure the OIDC settings:
- Response Type: Code
- Authentication Method: Basic
- Grant Types: Authorization Code, Refresh Token
- Under Redirect Settings add the redirect URI:
https://vaultwarden.home.example.com/identity/connect/oidc-signin - 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