Self-Hosting Vaultwarden: Lightweight Bitwarden Server with Docker

Password managers are the single most impactful security tool you can adopt, but trusting a cloud service with every credential you own requires a leap of faith. Bitwarden is open source and well-audited, which helps — but if you’d rather keep your vault entirely under your control, Vaultwarden makes that practical.

Vaultwarden (formerly bitwarden_rs) is an unofficial Bitwarden-compatible server written in Rust. It implements the Bitwarden API so you can use all the official Bitwarden clients — browser extensions, mobile apps, desktop apps, CLI — while running the server on hardware you control. The big difference from the official Bitwarden self-hosted server: Vaultwarden runs in a single container using around 30MB of RAM, while the official stack needs 8+ containers and 2GB+ of RAM. For a homelab or small team, that’s the difference between “runs on a Raspberry Pi” and “needs a dedicated VM.”

It supports nearly everything the official server does: organizations, collections, attachments, Bitwarden Send, Emergency Access, FIDO2/WebAuthn, and the admin panel. The main things you lose are the enterprise features like SCIM provisioning and SSO (though SSO support has been added experimentally).

Vaultwarden vs Other Password Managers

FeatureVaultwardenBitwarden (Official)1PasswordKeePass/KeePassXC
Open source✅ AGPLv3✅ Server: AGPLv3❌ Proprietary✅ GPLv2
Self-hostable✅ Single container✅ 8+ containers✅ Local files
RAM usage~30MB~2GB+N/A~50MB
Browser extensions✅ Official Bitwarden⚠️ Via KeePassXC-Browser
Mobile apps✅ Official Bitwarden✅ KeePassDX/Strongbox
Organizations✅ Unlimited free⚠️ Paid for >2 users✅ Paid❌ Shared files only
Passkeys/FIDO2⚠️ Limited
Bitwarden Send
Emergency Access✅ Premium
Sync methodServer APIServer APICloudFile sync (manual)
Audit history⚠️ Event logs✅ Full audit

Prerequisites

  • A Linux server with Docker and Docker Compose installed
  • A domain name pointing to your server (required for HTTPS — Bitwarden clients refuse unencrypted connections)
  • A reverse proxy with HTTPS (Caddy, Traefik, or Nginx Proxy Manager)
  • 512MB RAM minimum (Vaultwarden itself uses ~30MB, but you want headroom)

Directory Structure

vaultd.dwoeaacntrkvade/ern-/compose.yml#Persistentvaultdata(SQLiteDB,attachments,etc.)

Create the project directory:

mkdir -p ~/vaultwarden && cd ~/vaultwarden

Docker Compose Setup

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    env_file: .env
    volumes:
      - ./data:/data
    ports:
      - "127.0.0.1:8080:80"
    environment:
      - DOMAIN=${DOMAIN}
      - SIGNUPS_ALLOWED=${SIGNUPS_ALLOWED:-false}
      - ADMIN_TOKEN=${ADMIN_TOKEN}
      - SMTP_HOST=${SMTP_HOST}
      - SMTP_FROM=${SMTP_FROM}
      - SMTP_PORT=${SMTP_PORT:-587}
      - SMTP_SECURITY=${SMTP_SECURITY:-starttls}
      - SMTP_USERNAME=${SMTP_USERNAME}
      - SMTP_PASSWORD=${SMTP_PASSWORD}
      - LOG_FILE=/data/vaultwarden.log
      - LOG_LEVEL=warn
      - WEBSOCKET_ENABLED=true
      - SENDS_ALLOWED=true
      - EMERGENCY_ACCESS_ALLOWED=true
      - ORG_CREATION_USERS=all

Environment Configuration

Create the .env file:

# Domain — MUST include https://
DOMAIN=https://vault.yourdomain.com

# Signups — disable after creating your account(s)
SIGNUPS_ALLOWED=true

# Admin panel token — generate a strong one
# Use: openssl rand -base64 48
ADMIN_TOKEN=your-very-long-random-token-here

# SMTP for email verification and 2FA recovery
SMTP_HOST=smtp.fastmail.com
SMTP_FROM=[email protected]
SMTP_PORT=587
SMTP_SECURITY=starttls
SMTP_USERNAME=[email protected]
SMTP_PASSWORD=your-app-password

Generate a secure admin token:

openssl rand -base64 48

Since Vaultwarden 1.28+, you can also use Argon2id-hashed admin tokens for extra security:

# Install argon2 if needed: apt install argon2
echo -n "your-admin-password" | argon2 "$(openssl rand -base64 32)" -e -id -k 65540 -t 3 -p 4

Then set ADMIN_TOKEN to the full Argon2 hash string (starting with $argon2id$).

Deploy and Initial Setup

docker compose up -d

Verify it’s running:

docker compose logs -f vaultwarden

You should see Vaultwarden start on port 80 (mapped to 8080 on your host). Now set up your reverse proxy.

Reverse Proxy Configuration

Vaultwarden needs HTTPS — Bitwarden clients won’t connect over plain HTTP (except to localhost). You also need WebSocket support for real-time sync.

v}aultr.eyvoeurrsdeo_mparionx.ycolmoc{alhost:8080

That’s it. Caddy handles HTTPS automatically and proxies WebSocket connections without extra configuration.

Nginx

server {
    listen 443 ssl http2;
    server_name vault.yourdomain.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    client_max_body_size 525M;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /notifications/hub {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}

Creating Your Account

  1. Navigate to https://vault.yourdomain.com
  2. Click Create Account
  3. Use a strong master password — this is the one password you need to remember
  4. Verify your email if SMTP is configured
  5. Immediately disable signups after creating accounts for everyone who needs access:
# Edit .env
SIGNUPS_ALLOWED=false

# Restart
docker compose up -d

You can always re-enable signups temporarily or invite users through the admin panel.

Admin Panel

Access the admin panel at https://vault.yourdomain.com/admin and enter your admin token.

From here you can:

  • Manage users — invite, delete, disable accounts
  • View organizations — see all orgs and their members
  • Server settings — modify configuration without restarting
  • Diagnostics — check server health, DNS, HTTPS, and time sync
  • Invite users — send email invitations even with signups disabled

The admin panel is powerful — protect it. Consider limiting access by IP in your reverse proxy:

#v}aCualdtr@h}d.eaayyvdnoemdeuril@rxrsnebeadelsmo_p@oppmpaacolartdkneiohmednxid.y/n@canbrold{olemomtoscict{anrkrl*eeihmdcootst4te0a:_3d8im0pi8n019t2o.1l6o8c.a0l.0n/e1t6wo1r0k.0.0.0/8

Organizations and Sharing

Organizations let you share passwords with family or team members:

  1. In the web vault, go to OrganizationsNew Organization
  2. Name it (e.g., “Family” or “Team”)
  3. Create Collections to group credentials (e.g., “Streaming”, “Banking”, “Infrastructure”)
  4. Invite members by email — they’ll get an invitation to join
  5. Assign collection access per user with read/write permissions

Unlike official Bitwarden, Vaultwarden gives you unlimited organizations and collections for free — no premium plan needed.

Two-Factor Authentication

Protect your vault with 2FA — Vaultwarden supports multiple methods:

  • TOTP (Authenticator app) — works out of the box, no config needed
  • FIDO2/WebAuthn — hardware keys like YubiKey (requires HTTPS, which you already have)
  • Email codes — requires SMTP configuration (already done above)
  • Duo Security — requires a Duo account and API credentials

Enable 2FA in your account settings → SecurityTwo-step Login. TOTP is the easiest to set up — scan the QR code with your authenticator app and save the recovery code somewhere safe (printed, not in the vault itself).

Strongly recommended: Enable at least TOTP for every account on your instance. A compromised master password without 2FA means complete vault access.

Bitwarden Send

Bitwarden Send lets you securely share text or files with anyone — even people without a Bitwarden account. Shared items can have:

  • Expiration dates
  • Maximum access counts
  • Password protection
  • Auto-deletion after the expiry

It’s useful for sharing credentials with contractors, sending sensitive documents, or creating self-destructing notes. Access it from any Bitwarden client under Send.

Backups

Your vault data is precious — losing it means losing every password. Vaultwarden stores everything in the ./data directory:

  • db.sqlite3 — the main database (all vault entries, users, orgs)
  • attachments/ — file attachments
  • sends/ — Bitwarden Send files
  • config.json — admin panel settings
  • rsa_key* — RSA keypair for authentication tokens

Automated Backup Script

#!/bin/bash
# backup-vaultwarden.sh
BACKUP_DIR="/backups/vaultwarden"
DATA_DIR="$HOME/vaultwarden/data"
DATE=$(date +%Y-%m-%d_%H%M)
KEEP_DAYS=30

mkdir -p "$BACKUP_DIR"

# Stop to ensure consistent SQLite state (or use sqlite3 .backup)
docker compose -f ~/vaultwarden/docker-compose.yml stop vaultwarden

tar czf "$BACKUP_DIR/vaultwarden-$DATE.tar.gz" -C "$DATA_DIR" .

docker compose -f ~/vaultwarden/docker-compose.yml start vaultwarden

# Alternative: hot backup without stopping (requires sqlite3)
# sqlite3 "$DATA_DIR/db.sqlite3" ".backup '$BACKUP_DIR/db-$DATE.sqlite3'"

# Cleanup old backups
find "$BACKUP_DIR" -name "vaultwarden-*.tar.gz" -mtime +$KEEP_DAYS -delete

echo "Backup complete: vaultwarden-$DATE.tar.gz"

For zero-downtime backups, use the SQLite .backup command instead of stopping the container. Better yet, combine both: use .backup for frequent hot backups and a full stop-and-tar weekly.

Critical: Store backups off-site. If the server dies, you need those passwords. Use Kopia, Duplicati, or even a simple rsync to a second location.

Security Hardening

Disable Unused Features

If you don’t need certain features, disable them to reduce attack surface:

# In .env
SENDS_ALLOWED=false          # Disable Bitwarden Send
EMERGENCY_ACCESS_ALLOWED=false  # Disable Emergency Access
ORG_CREATION_USERS=none      # Only admin can create organizations
SIGNUPS_ALLOWED=false         # Disable public registration
INVITATIONS_ALLOWED=true      # Admin invitations only
SHOW_PASSWORD_HINT=false      # Don't expose password hints

Rate Limiting and Login Protection

Vaultwarden has built-in login attempt limiting:

LOGIN_RATELIMIT_SECONDS=60
LOGIN_RATELIMIT_MAX_BURST=10
ADMIN_RATELIMIT_SECONDS=60
ADMIN_RATELIMIT_MAX_BURST=3

Fail2Ban Integration

Add a Fail2Ban jail to block brute-force attempts at the IP level:

# /etc/fail2ban/filter.d/vaultwarden.conf
[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
ignoreregex =
# /etc/fail2ban/jail.d/vaultwarden.local
[vaultwarden]
enabled = true
port = 80,443
filter = vaultwarden
action = %(action_mwl)s
logpath = /path/to/vaultwarden/data/vaultwarden.log
maxretry = 5
bantime = 14400
findtime = 14400

Network Isolation

Only expose Vaultwarden through your reverse proxy — never directly to the internet:

# In docker-compose.yml, bind only to localhost
ports:
  - "127.0.0.1:8080:80"

Or better, use a Docker network shared with your reverse proxy and skip port binding entirely.

Migrating from Bitwarden or Other Managers

From Bitwarden Cloud or Other Instances

  1. In your current Bitwarden, go to ToolsExport Vault
  2. Choose JSON format (encrypted if available)
  3. In your Vaultwarden web vault, go to ToolsImport Data
  4. Select the format and upload

From Other Password Managers

Bitwarden’s import supports 50+ formats including LastPass, 1Password, KeePass, Dashlane, Chrome, and Firefox. Export from your current manager, then import into your Vaultwarden web vault.

Important: Delete the exported file immediately after import — it contains all your passwords in plain text (unless you used encrypted export).

Client Configuration

All official Bitwarden clients work with Vaultwarden. You just need to point them to your server:

  1. Open any Bitwarden client (browser extension, mobile app, desktop)
  2. On the login screen, click the gear icon or Self-hosted option
  3. Enter your server URL: https://vault.yourdomain.com
  4. Log in with your credentials

The setting persists — you only need to configure the server URL once per client.

Troubleshooting

Clients won’t connect: Vaultwarden requires HTTPS. Verify your certificate is valid and the domain resolves correctly. Check https://vault.yourdomain.com/alive — it should return a 200 response.

WebSocket sync not working: Ensure your reverse proxy passes WebSocket upgrades for the /notifications/hub path. In Caddy this works automatically; Nginx needs explicit Upgrade headers.

Email not sending: Test SMTP from the admin panel’s diagnostics page. Common issues: wrong port (use 587 for STARTTLS, 465 for implicit TLS), app-specific password needed (Gmail, Fastmail), or firewall blocking outbound SMTP.

“Invalid admin token”: If using an Argon2 hash, ensure the full hash string is in your .env file and special characters are properly escaped. Try wrapping the value in single quotes.

Attachments failing: Check that the data/ directory is writable and has enough disk space. The client_max_body_size in Nginx must be large enough (525M matches the Bitwarden default).

High memory after long uptime: Vaultwarden is generally very lean, but if memory creeps up, simply restart the container. It recovers in seconds.

Push notifications not working: Vaultwarden 1.29+ supports official Bitwarden push notifications. You need to register for free push notification IDs at https://bitwarden.com/host/ and set PUSH_ENABLED=true with the provided PUSH_INSTALLATION_ID and PUSH_INSTALLATION_KEY in your environment.

Power User Tips

  • Push notifications: Register at bitwarden.com/host for free push relay IDs — gives you instant sync instead of periodic polling
  • YubiKey OTP: Besides FIDO2, Vaultwarden supports YubiKey OTP validation with a Yubico API key
  • Multiple domains: Set DOMAIN to your primary domain; clients can connect via any domain that reaches the server
  • Database backend: SQLite works great for most users, but Vaultwarden also supports PostgreSQL and MySQL if you prefer
  • Icon caching: Vaultwarden fetches website icons for your vault entries — set ICON_CACHE_TTL=2592000 for monthly refresh
  • Vault health reports: The web vault includes reports for exposed passwords (Have I Been Pwned integration), reused passwords, weak passwords, and unsecured websites
  • CLI automation: Use the Bitwarden CLI (bw) for scripting — generate passwords, look up entries, or populate .env files from vault items
  • Browser extension auto-fill: Configure the browser extension to auto-fill on page load for trusted sites — saves time without compromising security on unknown sites
  • Emergency Access: Designate a trusted person who can request access to your vault after a configurable waiting period — useful for estate planning