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
| Feature | Vaultwarden | Bitwarden (Official) | 1Password | KeePass/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 method | Server API | Server API | Cloud | File 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
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.
Caddy (Recommended)
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
- Navigate to
https://vault.yourdomain.com - Click Create Account
- Use a strong master password — this is the one password you need to remember
- Verify your email if SMTP is configured
- 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:
Organizations and Sharing
Organizations let you share passwords with family or team members:
- In the web vault, go to Organizations → New Organization
- Name it (e.g., “Family” or “Team”)
- Create Collections to group credentials (e.g., “Streaming”, “Banking”, “Infrastructure”)
- Invite members by email — they’ll get an invitation to join
- 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 → Security → Two-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 attachmentssends/— Bitwarden Send filesconfig.json— admin panel settingsrsa_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
- In your current Bitwarden, go to Tools → Export Vault
- Choose JSON format (encrypted if available)
- In your Vaultwarden web vault, go to Tools → Import Data
- 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:
- Open any Bitwarden client (browser extension, mobile app, desktop)
- On the login screen, click the gear icon or Self-hosted option
- Enter your server URL:
https://vault.yourdomain.com - 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
DOMAINto 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=2592000for 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.envfiles 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
Related Guides
- Running Caddy Server: The Zero-Config HTTPS Reverse Proxy
- Linux Server Hardening Checklist for Self-Hosters
- The Complete Self-Hosting Security Stack: Fail2Ban + CrowdSec + Authelia
- Self-Hosting Kopia: Modern Backup Solution vs Restic
- Docker Volume Management: Backups, Migration, and Best Practices
- Running Authentik vs Authelia: SSO Comparison 2026