vaultwarden

Installation
SKILL.md

Identity

  • Image: vaultwarden/server (Docker Hub)
  • Config: Environment variables passed to the container (no separate config file)
  • Data dir: /data inside container (mount a host volume here)
  • Database: SQLite at /data/db.sqlite3 by default; PostgreSQL or MySQL optional
  • Port: 80/tcp (HTTP inside container — always put HTTPS termination in front)
  • Admin panel: /admin path (disabled by default; requires ADMIN_TOKEN env var)
  • Websocket port: 3012/tcp (live sync; must be proxied alongside port 80)

Key Operations

Operation Command
Check container status docker ps | grep vaultwarden
View live logs docker logs -f vaultwarden
View last 100 log lines docker logs --tail 100 vaultwarden
Restart container docker restart vaultwarden
Stop / start docker stop vaultwarden / docker start vaultwarden
Inspect environment variables docker inspect vaultwarden | python3 -m json.tool | grep -A1 Env
Backup data directory tar czf vaultwarden-backup-$(date +%Y%m%d).tar.gz -C /opt/vaultwarden data/
Restore from backup docker stop vaultwarden && tar xzf vaultwarden-backup-<date>.tar.gz -C /opt/vaultwarden && docker start vaultwarden
Enable admin panel Set ADMIN_TOKEN env var and recreate container
Generate admin token (argon2) docker run --rm -it vaultwarden/server /vaultwarden hash --preset owasp
List users (admin API) curl -s -H "Authorization: Bearer <token>" https://vault.example.com/admin/users
Force sync all clients Admin panel → Users → click user → Deauthorize Sessions
Update container image docker pull vaultwarden/server && docker compose up -d
Disable new signups Set SIGNUPS_ALLOWED=false and recreate container
Check email config Send test email via admin panel → Diagnostics → SMTP → Send test email
SQLite integrity check docker exec vaultwarden sqlite3 /data/db.sqlite3 'PRAGMA integrity_check;'

Expected Ports

  • 80/tcp — HTTP inside the container (never expose this directly; terminate TLS upstream)
  • 3012/tcp — WebSocket for live sync (must be proxied; clients will poll without it but sync is delayed)
  • Bitwarden/Vaultwarden clients refuse plaintext HTTP — HTTPS with a valid cert is required
  • Verify container is listening: docker port vaultwarden

Health Checks

  1. docker inspect --format='{{.State.Status}}' vaultwardenrunning
  2. curl -sf https://vault.example.com/alive → HTTP 200 (Vaultwarden health endpoint)
  3. curl -sf https://vault.example.com/api/config → JSON response with version field

Common Failures

Symptom Likely cause Check / Fix
Clients show "Invalid server URL" or refuse to connect HTTP instead of HTTPS — clients enforce TLS Set up a reverse proxy with a valid cert; DOMAIN env var must be the HTTPS URL
Admin panel returns 404 ADMIN_TOKEN not set or container not recreated after adding it Set ADMIN_TOKEN, then docker compose up -d to recreate (not restart)
Email invites / 2FA emails not delivered SMTP not configured or wrong credentials Check SMTP_* env vars; use admin panel Diagnostics → Send test email
Attachment uploads fail or are missing /data volume not mounted or wrong path Verify volume mount in compose file; check DATA_FOLDER env var matches mount
"Database is locked" in logs Concurrent SQLite writes (e.g., multiple containers or backup tool hitting DB live) Stop container before backup; or migrate to PostgreSQL for multi-user deployments
Mobile push notifications not working PUSH_ENABLED not set or Bitwarden relay account not configured Register at https://bitwarden.com/host/; add PUSH_INSTALLATION_ID and PUSH_INSTALLATION_KEY
WebSocket errors in browser console Port 3012 not proxied or WebSocket upgrade headers missing Add WebSocket proxy block to nginx/Caddy config; confirm port 3012 forwarded

Pain Points

  • HTTPS is mandatory: Bitwarden-compatible clients enforce HTTPS at the protocol level. A self-signed certificate will cause connection failures unless the cert is manually trusted on every device. Use Let's Encrypt via Caddy or Certbot.
  • Admin panel is off by default: The /admin endpoint only activates when ADMIN_TOKEN is set. Use the argon2 hash form (vaultwarden hash) rather than a plaintext token — plaintext works but logs a warning every startup.
  • Container must be recreated (not restarted) after env var changes: docker restart does not re-read environment variables. Changes to the compose file require docker compose up -d to recreate the container.
  • SQLite is sufficient for personal or small-team use: The single-writer limitation rarely matters at low concurrency, but if you see "database is locked" under normal use, migrate to PostgreSQL via DATABASE_URL.
  • Regular /data backups are non-negotiable: The entire Vaultwarden state (vault DB, attachments, config, RSA keys) lives in /data. A missing or stale backup means unrecoverable vault loss. Automate backups with a cron job or a sidecar container.
  • Disable signups after initial user creation: SIGNUPS_ALLOWED=true (the default) lets anyone register. Set SIGNUPS_ALLOWED=false immediately after creating your accounts, or use SIGNUPS_DOMAINS_WHITELIST to restrict by email domain.
  • DOMAIN must match the actual HTTPS URL: Vaultwarden uses this for generating invitation links, TOTP URIs, and push notification callbacks. A mismatch causes broken invite emails and client errors even when login works.

References

See references/ for:

  • docker-compose.yml.annotated — complete compose file with every environment variable explained
  • docs.md — official documentation and community links
Related skills
Installs
1
GitHub Stars
5
First Seen
Mar 18, 2026