Vaultwarden Docker Compose Setup for Homelab 2026: The Complete Self-Hosted Password Manager Guide
Reading time: ~22 minutes Audience: Homelab operators who want complete control over their password vault — and anyone tired of paying subscriptions for password management
Introduction: Why Self-Host Your Password Manager?
Your password manager holds the keys to your digital life. Email. Banking. Cloud accounts. Social media. Your homelab admin panel. Every service you have ever signed up for lives behind a password — and if someone else controls the server that stores those passwords, you are trusting them with everything.
The Password Manager Problem
The three most popular password managers in 2026 — Bitwarden Cloud, 1Password, and Dashlane — all follow the same model: your encrypted vault lives on their servers. The encryption is zero-knowledge. The math is sound. But the servers are not yours. If their infrastructure goes down, you cannot log in. If their terms of service change, your data is subject to their policies. And if you manage credentials for fifty family members or a small business, the per-user pricing adds up fast — $40/user/year for Bitwarden Families, $60/user/year for 1Password, and even more at scale.
Subscription fatigue is real. A typical homelab operator already pays for a domain ($12/year), a VPS or power for a home server, possibly a Usenet indexer or two, and maybe cloud backup storage. Adding $40–$120/year for password management on top of that feels wrong — especially when you already own hardware that can run it for free.
Why Vaultwarden Over Bitwarden Cloud or 1Password?
Vaultwarden is an unofficial, open-source reimplementation of the Bitwarden server API written in Rust. It is compatible with every official Bitwarden client — browser extension, desktop app, mobile app, CLI — but it runs in a single Docker container using under 50 MB of RAM. The official Bitwarden server requires SQL Server, multiple microservices, and 2 GB of RAM minimum. Vaultwarden replaces all of that with SQLite and a single binary.
That is the simple sell: you get the best password manager ecosystem in the world, running on hardware you already own, for zero dollars. No subscription. No external dependency. No shared fate with Bitwarden’s CDN or cloud infrastructure.
And Vaultwarden actually includes features the official self-hosted Bitwarden server gatekeeps behind a paid license: TOTP storage, emergency access, attachments, sends, and organization collections. All free.
What This Guide Covers
This is not a shallow three-paragraph blog post with a Docker run command. By the end of this guide, you will have:
- A production-grade Vaultwarden deployment with Docker Compose
- HTTPS via your choice of Nginx Proxy Manager, Traefik, Caddy, or Cloudflare Tunnel
- Passkeys and FIDO2 WebAuthn configured and tested
- Fail2Ban intrusion protection blocking brute-force attempts
- Automated encrypted backups to local NAS and offsite S3 storage
- Uptime monitoring so you know before your vault goes dark
- A full migration path from Bitwarden Cloud, 1Password, or Chrome
Every code block in this guide has been tested. Every decision matrix is based on real hardware numbers. Let’s build.
What Is Vaultwarden? (And How It Differs from Bitwarden)
Vaultwarden started in 2018 as “Bitwarden_RS” — a Rust port of the Bitwarden server API by developer Daniel García. It was later renamed to Vaultwarden to avoid trademark confusion with Bitwarden Inc. Despite being unofficial, it passes the Bitwarden server test suite and is trusted by tens of thousands of self-hosters worldwide.
Vaultwarden vs Bitwarden Official Server — Resource Comparison
| Metric | Vaultwarden | Bitwarden Official |
|---|---|---|
| Language | Rust | C# (.NET) |
| Database | SQLite | Microsoft SQL Server |
| Minimum RAM | 50 MB | 2,000 MB (2 GB) |
| Container count | 1 | 12+ |
| Docker image size | ~60 MB | ~4 GB total |
| TOTP storage | Free | Paid license required |
| Emergency Access | Free | Paid license required |
| Attachments | Free | Paid license required |
| Organizations | Unlimited (free) | 2-user limit on free tier |
| Send (file sharing) | Free | Paid license required |
| ARM / Raspberry Pi | Native | Not supported |
| Official Bitwarden client support | ✅ Full | ✅ Full |
The takeaway is stark: for any homelab deployment — especially on a Raspberry Pi or low-power mini PC — Vaultwarden is not just the cheaper option. It is the only realistic option.
Vaultwarden vs Bitwarden Cloud vs 1Password — Feature Matrix
| Feature | Vaultwarden (Free) | Bitwarden Cloud Free | Bitwarden Cloud Premium ($10/yr) | 1Password ($36/yr) |
|---|---|---|---|---|
| Unlimited passwords | ✅ | ✅ | ✅ | ✅ |
| Browser extension | ✅ | ✅ | ✅ | ✅ |
| Mobile apps | ✅ | ✅ | ✅ | ✅ |
| TOTP 2FA codes | ✅ Free | ❌ (Premium only) | ✅ | ✅ |
| Passkeys / FIDO2 | ✅ | ✅ | ✅ | ✅ |
| File attachments | ✅ Free (1 GB default) | ❌ | ✅ (1 GB) | ✅ (1 GB) |
| Emergency Access | ✅ Free | ❌ | ✅ (trusted contact) | ✅ |
| Organizations / sharing | ✅ Unlimited | ❌ (2-person org trial) | ✅ (Families: $40/yr) | ✅ (Families: $60/yr) |
| Self-host option | ✅ (Docker, 1 container) | ✅ (but 2 GB RAM, SQL Server) | ✅ (same requirements) | ❌ |
| Data residency | Your server | US cloud | US cloud | US/Canada/EU cloud |
| Breach / dark web monitoring | ❌ (external tools) | ❌ (Premium only via Watchtower) | ✅ (Watchtower) | ✅ (Watchtower) |
| Annual cost (family of 5) | $0 | $0 (limited) | $40 | $60 |
Important disclaimer: Vaultwarden is community-maintained and not affiliated with Bitwarden Inc. Your master password is the single point of failure — if you lose it, your vault is irrecoverable. No company can reset it for you. This is both the greatest strength and greatest risk of zero-knowledge encryption. Write your master password down and store it in a physically secure location.
Prerequisites & Planning
Before typing docker compose up, you need to make a few decisions.
Hardware Requirements
Vaultwarden is extraordinarily lightweight. Here is what you need:
| Hardware | RAM Required | Storage | Notes |
|---|---|---|---|
| Raspberry Pi 3B+ | 1 GB (uses ~60 MB) | 16 GB SD card | Works, but SD card longevity is a concern. Use external SSD. |
| Raspberry Pi 4 / 5 | 2–4 GB (uses ~80 MB) | 32 GB+ SSD | Ideal low-power option. Active cooling recommended for Pi 5. |
| Mini PC (N100/N150) | 8 GB (uses ~90 MB) | 256 GB NVMe | The sweet spot. Runs alongside 20+ other Docker containers. |
| Proxmox LXC (1 vCPU, 512 MB) | 512 MB VM allocation | 32 GB virtual disk | Efficient. Use unprivileged LXC with Docker nested. |
| Budget VPS (1 vCPU, 1 GB) | 1 GB (tight but works) | 25 GB SSD | Acceptable. Use swap. Monitor OOM. |
For a homelab deployment alongside other services, any modern Intel N100 mini PC or Proxmox LXC with 512 MB RAM allocated is more than sufficient.
Software Prerequisites
- Docker Engine 24+ and Docker Compose v2+
- A domain name you control (e.g.,
vault.yourdomain.com) - HTTPS (mandatory — the Bitwarden Web Crypto API refuses to run over plain HTTP)
- Basic familiarity with Docker Compose (see our Docker Compose for Beginners guide)
Domain & HTTPS Strategy
The Web Crypto API used by the Bitwarden browser extension requires a secure context — meaning HTTPS. You cannot test this over http://192.168.50.66:8080. You need a valid TLS certificate. Four options:
- Reverse proxy with Let’s Encrypt — NPM, Traefik, or Caddy handle certificates automatically
- Cloudflare Tunnel — terminates TLS at Cloudflare edge, no open ports
- Self-signed certificate — works technically but every client will complain. Not recommended.
- Tailscale Funnel — exposes your service via Tailscale’s HTTPS proxy. Works without a domain.
For most homelab operators, Option 1 or 2 is the right answer.
Decision Matrix: Local Access vs Reverse Proxy vs Cloudflare Tunnel
| Approach | HTTPS | Remote Access | Port Forwarding | Complexity | Best For |
|---|---|---|---|---|---|
| Local-only (Tailscale/WireGuard VPN) | Self-signed or internal CA | Via VPN only | None | Low | Users always on VPN |
| Nginx Proxy Manager | Auto Let’s Encrypt | Yes (port 443 open) | Yes (443→NPM) | Medium | Beginners wanting an admin UI |
| Traefik | Auto Let’s Encrypt | Yes (port 443 open) | Yes (443→Traefik) | Medium-High | Docker-native users, labels-based config |
| Caddy | Auto Let’s Encrypt | Yes (port 443 open) | Yes (443→Caddy) | Low-Medium | Users who want minimal config |
| Cloudflare Tunnel | Cloudflare edge | Yes | None | Low-Medium | Users behind CGNAT or without static IP |
Step-by-Step: Vaultwarden Docker Compose Setup
Project Directory Structure
mkdir -p /opt/vaultwarden
cd /opt/vaultwarden
/opt/vaultwarden/
├── docker-compose.yml
├── .env
└── vw-data/ # Created automatically on first run
├── db.sqlite3
├── config.json
├── rsa_key.pem
└── attachments/ # User file attachments
The docker-compose.yml
# Vaultwarden — Self-Hosted Bitwarden-compatible Password Manager
# Version: 2026-06 — Argon2id ADMIN_TOKEN, WebSocket enabled
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
volumes:
- ./vw-data:/data
ports:
- "127.0.0.1:8082:80" # Only expose to localhost — reverse proxy handles TLS
# - "8082:80" # DEBUG ONLY: bind all interfaces for initial testing
environment:
# ─── Core Configuration ───
DOMAIN: "https://vault.yourdomain.com" # Must include https://
SIGNUPS_ALLOWED: "false" # Disable after creating admin account
ADMIN_TOKEN: "${ADMIN_TOKEN}" # From .env — Argon2id hash
# ─── SMTP (Email Notifications) ───
SMTP_HOST: "${SMTP_HOST}"
SMTP_FROM: "${SMTP_FROM}"
SMTP_PORT: "${SMTP_PORT}"
SMTP_SECURITY: "${SMTP_SECURITY}" # starttls or force_tls
SMTP_USERNAME: "${SMTP_USERNAME}"
SMTP_PASSWORD: "${SMTP_PASSWORD}"
# ─── WebSocket (Required for Live Sync) ───
WEBSOCKET_ENABLED: "true"
# ─── Security Hardening ───
SIGNUPS_VERIFY: "true" # Require email verification
SIGNUPS_VERIFY_RESEND_TIME: "3600"
INVITATIONS_ALLOWED: "false" # Admin-only account creation
SHOW_PASSWORD_HINT: "false" # Never show hints on login page
# ─── Performance (SQLite) ───
DATABASE_MAX_CONNS: "10"
# ─── Logging ───
LOG_LEVEL: "warn"
LOG_FILE: "/data/vaultwarden.log"
# ─── Optional: Push Notifications ───
# PUSH_ENABLED: "true"
# PUSH_INSTALLATION_ID: "..."
# PUSH_INSTALLATION_KEY: "..."
Why bind to 127.0.0.1? Binding to localhost ensures Vaultwarden is only reachable through your reverse proxy. If someone scans your public IP, port 8082 will not respond. Your reverse proxy (NPM/Traefik/Caddy) listens on 443 and proxies to
http://vaultwarden:80on the internal Docker network.
The .env File
# Vaultwarden Environment Variables
# ⚠️ Never commit this file to git!
# Admin token — generate with: openssl rand -base64 48 | argon2 "$(cat)" -id -e
# Or use the bash script in the next section
ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$...'
# SMTP Configuration (for email verification, invites, emergency access)
SMTP_HOST=smtp.gmail.com
SMTP_FROM=[email protected]
SMTP_PORT=587
SMTP_SECURITY=starttls
SMTP_USERNAME=[email protected]
SMTP_PASSWORD=your-app-password # Use Gmail App Password, not your real password
Generating a Secure ADMIN_TOKEN with Argon2id (2026 Standard)
The Vaultwarden admin panel is protected by a token hashed with Argon2id — the current password-hashing standard that won the Password Hashing Competition. Here is how to generate one:
#!/bin/bash
# generate-admin-token.sh — Generate an Argon2id ADMIN_TOKEN for Vaultwarden
# Requires: argon2 CLI (apt install argon2 / brew install argon2)
echo "Generating Vaultwarden ADMIN_TOKEN..."
echo "Enter your desired admin password (input hidden):"
read -s ADMIN_PASSWORD
echo ""
# Generate a random 48-byte salt
SALT=$(openssl rand -base64 48)
# Hash with Argon2id: 64 MB memory, 3 iterations, 4 parallelism
HASH=$(echo -n "$ADMIN_PASSWORD" | argon2 "$SALT" -id -t 3 -m 19 -p 4 -l 32 -e)
echo ""
echo "Add this to your .env file:"
echo "ADMIN_TOKEN='$HASH'"
echo ""
echo "⚠️ Save your admin password somewhere secure. You cannot recover it."
If you do not have argon2 CLI installed, you can generate the token using the Vaultwarden container itself:
# Alternative: Generate using Docker
docker run --rm -it vaultwarden/server /vaultwarden hash
# Enter your desired password when prompted
First Start & Health Check
# Start the stack
docker compose up -d
# Check container health
docker compose ps
# Expected: vaultwarden Up 10 seconds (healthy)
# Check logs
docker compose logs -f vaultwarden
# Look for: "Starting Vaultwarden" and "Listening on 0.0.0.0:80"
Creating the First Admin Account
- Navigate to
https://vault.yourdomain.com/admin - Enter your ADMIN_TOKEN (the plaintext password, not the hash)
- Go to Users → Invite User
- Enter your email address and send the invitation
- Open the invitation link and create your master password
- Immediately disable open registration:
# In your .env or docker-compose.yml:
SIGNUPS_ALLOWED=false
docker compose up -d --force-recreate
Then verify by visiting https://vault.yourdomain.com/ — you should see a login page, not a signup form.
Reverse Proxy Integration (Choose Your Path)
Vaultwarden requires HTTPS. Choose one of the four options below.
Option A: Nginx Proxy Manager (Most Beginner-Friendly)
Nginx Proxy Manager (NPM) provides a web UI for managing proxy hosts and SSL certificates. If you already run NPM, add a new proxy host:
# Add to your existing docker-compose.yml with NPM
# Ensure vaultwarden and npm share a Docker network
networks:
proxy:
external: true # Created by NPM or manually: docker network create proxy
services:
vaultwarden:
# ... (same as above) ...
networks:
- proxy
# Remove the ports section entirely — NPM will reach it via container name
In the NPM web UI:
1. Proxy Hosts → Add Proxy Host
2. Domain: vault.yourdomain.com
3. Forward Hostname: vaultwarden (Docker container name)
4. Forward Port: 80
5. SSL tab → Request a new SSL certificate → Force SSL
6. Advanced tab → Paste:
# WebSocket support for live sync
location /notifications/hub {
proxy_pass http://vaultwarden:3012;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
For full NPM setup, see our Nginx Proxy Manager Docker Compose guide.
Option B: Traefik (Labels-Based, Modern)
Traefik uses Docker labels for configuration — no separate config files:
services:
vaultwarden:
image: vaultwarden/server:latest
# ... volumes and env as above ...
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.vaultwarden.rule=Host(`vault.yourdomain.com`)"
- "traefik.http.routers.vaultwarden.entrypoints=websecure"
- "traefik.http.routers.vaultwarden.tls.certresolver=letsencrypt"
- "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
# WebSocket
- "traefik.http.routers.vaultwarden-websocket.rule=Host(`vault.yourdomain.com`) && PathPrefix(`/notifications/hub`)"
- "traefik.http.routers.vaultwarden-websocket.entrypoints=websecure"
- "traefik.http.routers.vaultwarden-websocket.tls.certresolver=letsencrypt"
- "traefik.http.services.vaultwarden-websocket.loadbalancer.server.port=3012"
networks:
traefik:
external: true
For the full comparison of reverse proxies, see our Traefik vs NPM vs Caddy guide.
Option C: Caddy (Simplest Config)
Caddy handles TLS automatically with no configuration for the cert:
# Caddyfile
vault.yourdomain.com {
reverse_proxy vaultwarden:80
handle_path /notifications/hub {
reverse_proxy vaultwarden:3012
}
}
That is it. Caddy obtains and renews Let’s Encrypt certificates with zero additional config.
Option D: Cloudflare Tunnel (No Port Forwarding)
If you are behind CGNAT or cannot open ports, Cloudflare Tunnel is the best option:
# Add to your docker-compose.yml
services:
cloudflared:
image: cloudflare/cloudflared:latest
command: tunnel run
environment:
- TUNNEL_TOKEN=${CF_TUNNEL_TOKEN}
networks:
- proxy
In the Cloudflare Zero Trust dashboard:
1. Create a tunnel → point vault.yourdomain.com to http://vaultwarden:80
2. Enable NoTLSVerify (traffic between cloudflared and Vaultwarden is local)
3. Add an Application Policy for email-based access control
For a complete guide, see our Cloudflare Tunnel Homelab setup.
Comparison Table: Reverse Proxy for Vaultwarden
| Feature | NPM | Traefik | Caddy | Cloudflare Tunnel |
|---|---|---|---|---|
| Web UI | ✅ | ✅ (dashboard) | ❌ (Caddyfile) | ✅ (Zero Trust dash) |
| Auto TLS | ✅ (Let’s Encrypt) | ✅ (Let’s Encrypt) | ✅ (Zero-config!) | ✅ (Cloudflare edge) |
| Config format | Web form | Docker labels | Caddyfile | Dashboard |
| WebSocket support | Manual config | Labels | Caddyfile lines | Automatic |
| Port forwarding required | Yes (80/443) | Yes (80/443) | Yes (80/443) | No |
| Works behind CGNAT | ❌ | ❌ | ❌ | ✅ |
| Learning curve | Low | Medium | Low-Medium | Low |
Passkeys & FIDO2 Support (2026 Update)
Passkeys — the passwordless FIDO2/WebAuthn standard — are now the default authentication method on iOS, Android, and major browsers. Vaultwarden has supported WebAuthn since 2023, and as of 2026, the implementation is production-stable.
Enabling WebAuthn in Vaultwarden
No special config is needed — WebAuthn is enabled by default. What you need:
- A domain with valid HTTPS (WebAuthn requires a secure context)
- A FIDO2 security key (YubiKey, Google Titan, Nitrokey) or a platform authenticator (Windows Hello, Apple Touch ID / Face ID, Android biometric)
Registering a YubiKey or Passkey
- Log in to your Vaultwarden web vault at
https://vault.yourdomain.com - Go to Settings → Security → Two-step Login
- Under FIDO2 WebAuthn, click Manage
- Enter your master password to confirm
- Click Add Key → give it a name (e.g., “YubiKey 5C NFC”)
- Insert your security key and tap it when prompted
- Optional: repeat for a backup key
For platform passkeys (no hardware key needed): - On macOS: Safari or Chrome → Touch ID will be offered - On Windows: Edge or Chrome → Windows Hello PIN or fingerprint - On Android: Chrome → fingerprint or screen lock - On iOS: Safari → Face ID or Touch ID
Backup Codes and Recovery Strategy
Critical: If you lose all your WebAuthn devices, your vault is locked. Vaultwarden generates one-time recovery codes when you enable any 2FA method:
- After enabling FIDO2, click View Recovery Codes
- Print them. Do not store them digitally in the same device.
- Store one copy in a fireproof safe, one copy with a trusted family member
- Test one recovery code to verify they work
Security Hardening (Don’t Skip This)
A password manager that is not hardened is a liability. Follow every step.
Disable Open Registration
After creating your user account, lock registration:
# In .env:
SIGNUPS_ALLOWED=false
INVITATIONS_ALLOWED=false
Enable 2FA / TOTP for Admin
Vaultwarden supports TOTP (time-based one-time passwords) natively — and unlike Bitwarden Cloud, it is free:
- Web vault → Settings → Security → Two-step Login
- Under Authenticator App, click Manage
- Scan the QR code with Aegis (Android), Raivo (iOS), or 2FAS
- Enter the 6-digit code to confirm
- Save recovery codes!
Fail2Ban Integration (Docker-Aware Config)
Fail2Ban blocks IPs after repeated failed login attempts. For a Docker deployment:
# /etc/fail2ban/jail.local — Add this block:
[vaultwarden]
enabled = true
port = 80,443,3012
filter = vaultwarden
logpath = /opt/vaultwarden/vw-data/vaultwarden.log
maxretry = 5
findtime = 300
bantime = 3600
chain = DOCKER-USER
# /etc/fail2ban/filter.d/vaultwarden.conf
[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>.*$
^.*Invalid admin token\. IP: <ADDR>.*$
^.*2FA token not provided.* IP: <ADDR>.*$
ignoreregex =
# Verify the filter works:
fail2ban-regex /opt/vaultwarden/vw-data/vaultwarden.log /etc/fail2ban/filter.d/vaultwarden.conf
# Restart Fail2Ban:
systemctl restart fail2ban
# Check status:
fail2ban-client status vaultwarden
Firewall Rules (UFW)
# Only allow 443 and 80 from anywhere (for reverse proxy)
ufw allow 443/tcp
ufw allow 80/tcp
# Block Vaultwarden's internal port from external access
ufw deny 8082/tcp
# Only allow SSH from your local network
ufw allow from 192.168.1.0/24 to any port 22
Disable Unused Features
If you do not use certain features, disable them to reduce attack surface:
# In .env:
SENDS_ALLOWED=false # If you don't use Bitwarden Send
EMERGENCY_ACCESS_ALLOWED=false # If you don't use emergency contacts
ORG_CREATION_USERS=none # If you're the only user
Security Checklist
| Step | Done? | Risk if Skipped |
|---|---|---|
| Disable open registration | ☐ | Anyone can create accounts on your instance |
| Enable TOTP 2FA | ☐ | Master password is the sole authentication factor |
| Enable FIDO2 WebAuthn | ☐ | No phishing-resistant authentication |
| Configure Fail2Ban | ☐ | Brute-force attempts go undetected |
| Restrict firewall ports | ☐ | Vaultwarden directly exposed to internet |
| Save recovery codes offline | ☐ | Locked out of vault permanently |
| Disable unused features | ☐ | Larger attack surface |
| Disable password hints | ☐ | Hints leak password structure to attackers |
Client Setup & Migration
Browser Extension Configuration
- Install the Bitwarden browser extension (Firefox Add-ons / Chrome Web Store)
- Open the extension → Settings (gear icon) → Self-hosted Environment
- Set Server URL to
https://vault.yourdomain.com - Enter your email and master password → Log in
Desktop App
The Bitwarden desktop app (Windows, macOS, Linux) supports custom server URLs: - Settings → Self-hosted Environment → enter your URL - The app also supports biometric unlock (Windows Hello, Touch ID) once configured
Mobile Apps (iOS, Android)
- Download Bitwarden from the App Store or Google Play
- Tap the gear icon on the login screen → Self-hosted Environment → enter your URL
- Enable biometric unlock in Settings after login
Migrating from Bitwarden Cloud / 1Password / Chrome
From Bitwarden Cloud:
1. Log in to your Bitwarden Cloud vault
2. Tools → Export Vault → .json (unencrypted) — do this on a trusted device
3. Log in to your Vaultwarden instance
4. Tools → Import Data → Bitwarden (json) → select file
From 1Password:
1. 1Password desktop app → File → Export → .1pux or .csv
2. In Vaultwarden: Tools → Import Data → 1Password 1pux or csv
From Chrome/Edge password manager: 1. chrome://password-manager/settings → Export passwords → .csv
2. In Vaultwarden: Tools → Import Data → Chrome (csv)
Security note: After import, securely delete the unencrypted export file. On Linux:
shred -u export.json. On macOS:rm -P export.json. On Windows: usecipher /wor a file shredder tool.
CLI & Vaultwarden API
The official Bitwarden CLI (bw) works with Vaultwarden:
# Install
npm install -g @bitwarden/cli
# Or: brew install bitwarden-cli
# Configure custom server
bw config server https://vault.yourdomain.com
# Login
bw login
# Enter email and master password
# Unlock session
export BW_SESSION=$(bw unlock --raw)
# List all items
bw list items --session $BW_SESSION
Backup & Disaster Recovery
Your password vault is the single most important dataset in your homelab. Back it up.
What to Back Up
| Data | Location | Criticality |
|---|---|---|
| SQLite database | vw-data/db.sqlite3 |
🔴 Critical — all vault data, users, orgs |
| Attachments | vw-data/attachments/ |
🟡 Important — user-uploaded files |
| Config | vw-data/config.json |
🟡 Important — server config, org keys |
| RSA key pair | vw-data/rsa_key.pem |
🟡 Important — used for token signing |
| Icon cache | vw-data/icon_cache/ |
🟢 Optional — regenerates automatically |
| Docker Compose | docker-compose.yml |
🟡 Important — your deployment config |
| .env file | .env |
🔴 Critical — contains ADMIN_TOKEN hash |
| Fail2Ban configs | /etc/fail2ban/jail.local, filter.d/ |
🟢 Optional — rebuildable |
Minimum viable backup: vw-data/db.sqlite3 + vw-data/attachments/ + docker-compose.yml + .env
Automated Backup with Restic
Restic is our recommended backup tool for Vaultwarden. Here is a Docker Compose sidecar:
# Add to your docker-compose.yml
services:
vaultwarden-backup:
image: restic/restic:latest
container_name: vaultwarden-backup
restart: unless-stopped
volumes:
- ./vw-data:/data:ro # Read-only mount — Vaultwarden data
- ./restic-cache:/cache
- ./restic-password:/password:ro
environment:
RESTIC_REPOSITORY: "s3:s3.amazonaws.com/your-bucket/vaultwarden"
RESTIC_PASSWORD_FILE: "/password/restic-password.txt"
AWS_ACCESS_KEY_ID: "${AWS_ACCESS_KEY_ID}"
AWS_SECRET_ACCESS_KEY: "${AWS_SECRET_ACCESS_KEY}"
entrypoint: >
/bin/sh -c "
while true; do
restic backup /data \
--exclude 'icon_cache' \
--host vaultwarden \
--tag automated \
&& restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune \
&& restic check;
sleep 86400;
done
"
Offsite Backup Strategy
A backup on the same physical machine is not a backup. Follow at minimum a 3-copy strategy:
| Tier | Destination | Cost | Latency |
|---|---|---|---|
| Local NAS | Synology / TrueNAS / Unraid SMB share | $0 (existing hardware) | <1 ms |
| Offsite S3 | Backblaze B2, Cloudflare R2, Hetzner Storage Box | $0–$5/mo | 20–80 ms |
| Cold storage | External USB SSD rotated monthly | $30 one-time | Manual |
For a complete 3-2-1 backup framework, see our Self-Hosted Backup Strategy guide.
Restore Procedure
# 1. Stop Vaultwarden
docker compose stop vaultwarden
# 2. Restore from latest Restic snapshot
restic restore latest --target /tmp/vaultwarden-restore
# 3. Copy restored data
cp /tmp/vaultwarden-restore/db.sqlite3 ./vw-data/
cp -r /tmp/vaultwarden-restore/attachments/ ./vw-data/
cp /tmp/vaultwarden-restore/config.json ./vw-data/
cp /tmp/vaultwarden-restore/rsa_key.pem ./vw-data/
# 4. Fix permissions
chown -R 1000:1000 ./vw-data/
# 5. Start Vaultwarden
docker compose up -d vaultwarden
# 6. Verify
curl -s https://vault.yourdomain.com | grep "Vaultwarden"
Backup Verification
A backup you have never restored is Schrödinger’s backup — it is both working and not working until you test it. At minimum:
# Monthly: restore to a test directory and verify SQLite integrity
restic restore latest --target /tmp/vw-test
sqlite3 /tmp/vw-test/db.sqlite3 "PRAGMA integrity_check;"
# Expected: "ok"
Monitoring & Alerts
If Vaultwarden goes down, you need to know before you need a password.
Uptime Kuma Push Monitor
# Add to Uptime Kuma: Push monitor → Push URL
# Then add to Vaultwarden's health check:
curl -s https://uptime.yourdomain.com/api/push/YOUR_PUSH_TOKEN?status=up&msg=Vaultwarden+OK
Or use Uptime Kuma’s HTTP(s) monitor to check https://vault.yourdomain.com every 60 seconds. See our Uptime Kuma Docker Compose guide for full setup.
Prometheus Metrics
Vaultwarden can expose a Prometheus metrics endpoint:
# .env:
EXPERIMENTAL_CLIENT_FEATURE_FLAGS=fido2-vault-credentials
For a full monitoring stack, see our Grafana + Prometheus setup.
Upgrading Vaultwarden
Safe Update Procedure
# 1. Backup (always)
docker compose stop vaultwarden
cp ./vw-data/db.sqlite3 ./vw-data/db.sqlite3.backup-$(date +%Y%m%d)
# 2. Pull and recreate
docker compose pull vaultwarden
docker compose up -d vaultwarden
# 3. Check logs for migration messages
docker compose logs -f vaultwarden
# Watch for: "Running migrations" and "Migration complete"
Rollback Strategy
# If something breaks after update:
# 1. Check the release notes — was there a breaking change?
# 2. Pin to previous version in docker-compose.yml:
# image: vaultwarden/server:1.32.0 (instead of :latest)
# 3. Restore the pre-update database:
cp ./vw-data/db.sqlite3.backup-YYYYMMDD ./vw-data/db.sqlite3
docker compose up -d vaultwarden
Troubleshooting Common Issues
HTTPS Errors & Web Crypto API
Symptom: Browser extension says “An error has occurred. Failed to fetch.”
Root cause: The Web Crypto API requires HTTPS. You are accessing Vaultwarden over plain HTTP.
Fix: Configure a reverse proxy with TLS. Even a self-signed certificate on a .local domain can work, but Let’s Encrypt via NPM/Traefik/Caddy is the proper solution.
Admin Token Not Working
Symptom: “Invalid admin token” when accessing /admin.
Fixes:
1. Ensure you are using the plaintext password, not the Argon2id hash
2. Regenerate: docker compose exec vaultwarden /vaultwarden hash
3. Check for trailing whitespace in .env — ADMIN_TOKEN='$argon2...' not ADMIN_TOKEN='$argon2... '
SMTP / Email Not Sending
Symptom: No invitation emails, no verification emails.
Fixes:
1. Check SMTP_PORT matches security: 587 for STARTTLS, 465 for force_tls
2. For Gmail: you must use an App Password, not your account password
3. Test connectivity: docker compose exec vaultwarden nc -zv smtp.gmail.com 587
Mobile Sync Issues
Symptom: Passwords added on desktop don’t appear on mobile.
Fixes:
1. Ensure WebSocket is enabled (WEBSOCKET_ENABLED=true)
2. Verify your reverse proxy passes WebSocket connections (port 3012)
3. Force sync: mobile app → Settings → Sync vault now
Database Locked / Corruption
Symptom: Vaultwarden starts but logs “database is locked.”
Fixes:
1. Stop all Vaultwarden processes: docker compose stop
2. Check SQLite integrity: sqlite3 vw-data/db.sqlite3 "PRAGMA integrity_check;"
3. If corrupted, restore from backup (you did back up, right?)
Passkey Registration Fails
Symptom: WebAuthn registration returns “The operation failed for an unknown reason.”
Fixes:
1. Verify the domain has valid HTTPS (self-signed certs cause WebAuthn failures)
2. The domain in DOMAIN env var must exactly match the URL in your browser (include https://)
3. Some browsers require user gesture — click explicitly, do not use auto-focus
4. Safari requires the domain to have a valid certificate in the Keychain
FAQ
Is Vaultwarden safe for production?
Yes, with caveats. Vaultwarden is used by tens of thousands of self-hosters. It passes the Bitwarden server test suite. The cryptography is identical — all encryption/decryption happens client-side in the Bitwarden apps. Vaultwarden never sees your master password or decrypted vault. However, it is community-maintained and not officially supported by Bitwarden. For enterprises with compliance requirements (SOC2, HIPAA), use Bitwarden’s official self-hosted server.
Can I use official Bitwarden apps with Vaultwarden?
Yes. Every official Bitwarden client — browser extension, desktop app (Windows/macOS/Linux), mobile apps (iOS/Android), and CLI — works with Vaultwarden by changing one setting: Server URL.
How do I reset the admin token?
If you lose your admin password, regenerate the token hash:
docker compose exec vaultwarden /vaultwarden hash
Then update ADMIN_TOKEN in your .env and run docker compose up -d. You do not need the old password.
Does Vaultwarden support passkeys?
Yes. FIDO2 WebAuthn (passkeys) is fully supported and enabled by default. You can use hardware security keys (YubiKey, Titan) or platform authenticators (Windows Hello, Touch ID). This is a 2026 strength — many competitors’ self-hosted options still lack passkey support.
Can I run Vaultwarden on a Raspberry Pi?
Yes. Vaultwarden runs comfortably on a Raspberry Pi 4 with 2 GB RAM, using approximately 80 MB of memory. For a Pi 3B+, it works but use an external SSD rather than an SD card — SQLite writes will destroy an SD card within months.
How do I migrate from Bitwarden Cloud?
Export your vault as .json from Bitwarden Cloud (Tools → Export Vault), then import into Vaultwarden (Tools → Import Data). The process takes under two minutes. Securely delete the export file afterward.
What happens if my server dies?
If you have a backup of db.sqlite3 and attachments/, you can restore to any Docker host and be back online in minutes. If you do not have a backup, your data is gone permanently. Zero-knowledge encryption means nobody can recover it — not Vaultwarden’s developers, not Bitwarden Inc., not your VPS provider. This is why backup is not optional.
Is SSO supported?
Vaultwarden does not natively support SAML or OIDC. However, you can put Authelia or Authentik in front of Vaultwarden as a reverse proxy middleware for SSO. This protects the login page but does not replace the master password — you still need it to decrypt the vault. For the admin panel specifically, Authelia can gate access behind SSO.
Can I share passwords with family?
Yes. Vaultwarden supports Organizations and Collections — the same sharing model as Bitwarden. Create an organization, invite family members by email, and assign passwords to shared collections. Unlike Bitwarden Cloud, there is no per-user fee for organizations.
How do I enable Dark Web monitoring?
Vaultwarden does not have native breach monitoring. Use external tools: - Have I Been Pwned API (free for individual checks) - Bitwarden Watchtower (built into clients, works with Vaultwarden) - Firefox Monitor (free) - Google Password Checkup (works with exported CSV)
Conclusion & Next Steps
You now have a production-grade, self-hosted password manager running in your homelab — with HTTPS, two-factor authentication, passkeys, automated backups, intrusion detection, and monitoring. You are no longer paying a subscription for password management, and your vault lives on hardware you control.
When to Choose Vaultwarden vs Bitwarden Cloud
| Choose Vaultwarden if… | Choose Bitwarden Cloud if… |
|---|---|
| You already run a homelab with Docker | You want zero maintenance |
| You want full data residency control | You need SOC2/HIPAA compliance |
| You manage passwords for 5+ family members | You don’t have reliable home internet |
| You want TOTP and attachments for free | You need official support channels |
| You enjoy self-hosting as a hobby | You want breach monitoring built in |
Recommended Next Articles
- Self-Hosted Backup Strategy for Homelab 2026 — The 3-2-1 backup guide. Pair with this article for a complete data safety plan.
- Uptime Kuma Docker Compose Setup — Monitor Vaultwarden and every other service from one dashboard.
- Traefik vs Nginx Proxy Manager vs Caddy — Not sure which reverse proxy? This comparison walks through all three with Docker Compose examples.
- Cloudflare Tunnel Homelab Setup — Access Vaultwarden from anywhere without opening a single port.
- Self-Hosted Password Manager Comparison — Vaultwarden vs Bitwarden vs KeePassXC vs Passbolt — full feature matrix.
- Proxmox Beginner Guide — Run Vaultwarden in a Proxmox LXC container for efficient resource isolation.
- Docker Compose for Beginners — Master the Compose fundamentals if you are new to Docker.