Vaultwarden Self-Hosted Password Manager: The Ultimate Step-by-Step Deployment Guide for Homelabs
Deploy Vaultwarden on your homelab with Docker Compose: full guide covering requirements, secure setup, reverse proxy, SSL, backups, hardening, and troubleshooting.
Introduction
In the age of data breaches and mass surveillance, relying on cloud-based password managers like LastPass or Dashlane means trusting a third party with the keys to your digital kingdom. Even with encryption, zero-knowledge claims, and audits, the attack surface remains significant: server-side vulnerabilities, insider threats, or a company being acquired and changing its privacy policy. For privacy-conscious homelab enthusiasts, the solution is clear: self-host your own password manager. This gives you full control over your data, eliminates subscription costs, and teaches you valuable infrastructure skills along the way.
Enter Vaultwarden, a lightweight, community-driven implementation of the Bitwarden server API written in Rust. It's not an official Bitwarden product, but it's highly compatible with all official clients (desktop, mobile, browser extensions). Unlike the official Bitwarden server which requires hefty resources (MSSQL, .NET, etc.), Vaultwarden runs on a single binary with SQLite or PostgreSQL, consuming as little as 50 MB of RAM. This makes it perfect for a Raspberry Pi, a small Intel NUC, or an old laptop repurposed as a server.
This guide will walk you through a production-grade deployment of Vaultwarden using Docker Compose, covering everything from initial setup to advanced security hardening. You'll learn how to set up a reverse proxy with Nginx Proxy Manager or Caddy, enable SSL with Let's Encrypt, automate backups, and troubleshoot common pitfalls. By the end, you'll have a fully functional, secure, and private password manager that syncs seamlessly across all your devices — all running on your own hardware.
Let's get started. No prior Docker experience is required, but basic Linux command-line familiarity is assumed. We'll be using Ubuntu Server 22.04 LTS as the base OS, but the steps translate to Debian, Fedora, or even TrueNAS Scale with minor adjustments.
Prerequisites / Requirements
Before we dive into the installation, let's ensure your homelab meets the minimal requirements. Vaultwarden is incredibly resource-efficient, but you still need a stable environment. Below is a detailed table outlining hardware and software prerequisites.
| Component | Minimum | Recommended | Notes |
|---|---|---|---|
| CPU | 1 core | 2 cores (ARM or x86) | Any modern CPU works; ARM (Raspberry Pi 4) is fine. |
| RAM | 512 MB | 1 GB | Vaultwarden itself uses ~50-100 MB; leave room for OS and Docker. |
| Storage | 5 GB free | 10 GB+ | SSD preferred for better DB performance; HDD is acceptable for low traffic. |
| OS | Ubuntu 20.04+ / Debian 11+ | Ubuntu 22.04 LTS | Any Linux distribution with Docker support. |
| Docker | Docker 20.10+ | Docker 24+ | Install via official script or distro repo. |
| Docker Compose | v2 (plugin) | v2.20+ | Use docker compose (v2) not docker-compose (v1). |
| Domain Name | None (IP access) | A domain (e.g., vault.yourdomain.com) |
Required for SSL; you can use a free DuckDNS subdomain. |
| Ports | 80 & 443 (for reverse proxy) | Same | Vaultwarden itself runs on 8080 internally; you'll map it. |
| Reverse Proxy | None (direct IP) | Nginx Proxy Manager or Caddy | Handles SSL termination and request forwarding. |
Software to install before starting:
# Update system
sudo apt update && sudo apt upgrade -y
# Install Docker (official script)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Install Docker Compose plugin (if not included)
sudo apt install docker-compose-plugin -y
# Verify installations
docker --version
docker compose version
Step-by-Step Installation Guide
We'll build the entire stack step by step. Each step includes a full command or configuration block. Make sure to read each step completely before executing.
Step 1: Create Project Directory
Create a dedicated directory for Vaultwarden and its associated files. This keeps everything organized and simplifies backups.
mkdir -p ~/vaultwarden
cd ~/vaultwarden
mkdir -p ./data ./backups ./logs
Step 2: Create Docker Compose File
Create a docker-compose.yml file with the following content. This is the core of your deployment. We'll use the latest stable image vaultwarden/server:1.30.1 (as of early 2024). The file includes environment variables for security and functionality.
version: '3.8'
services:
vaultwarden:
image: vaultwarden/server:1.30.1
container_name: vaultwarden
restart: unless-stopped
environment:
# Required: domain for WebSocket and email links
DOMAIN: "https://vault.yourdomain.com"
# Enable secure cookies (important for HTTPS)
SIGNUPS_ALLOWED: "false" # Disable after creating your account
# Database: SQLite (default) or PostgreSQL (see advanced)
DATABASE_URL: "/data/db.sqlite3"
# SMTP settings for email verification (optional but recommended)
SMTP_HOST: "smtp.gmail.com"
SMTP_PORT: "587"
SMTP_SECURITY: "starttls"
SMTP_USERNAME: "your-email@gmail.com"
SMTP_PASSWORD: "your-app-password"
SMTP_FROM: "vaultwarden@yourdomain.com"
# Admin panel (disable after setup)
ADMIN_TOKEN: "CHANGE_ME_TO_A_LONG_RANDOM_STRING"
# Additional hardening
WEBSOCKET_ENABLED: "true"
ENABLE_DB_WAL: "true"
ICON_SERVICE: "internal" # Use internal icon fetcher
# Timezone
TZ: "UTC"
volumes:
- ./data:/data
- ./logs:/logs
ports:
- "127.0.0.1:8080:80" # Bind to localhost only; reverse proxy will forward
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
Important notes on the above:
- Change
DOMAINto your actual domain (orhttps://your-server-ip:portif using IP without SSL initially). - Set
SIGNUPS_ALLOWEDtotrueinitially so you can create your account, then set tofalseafter. ADMIN_TOKENmust be a long random string; generate one withopenssl rand -base64 48.- The port mapping
127.0.0.1:8080:80means Vaultwarden is only accessible from the host machine, not the network. This is a security best practice when using a reverse proxy. - If you don't have SMTP, remove those lines; you'll still be able to use Vaultwarden, but you won't get email verification.
Step 3: Start Vaultwarden and Create Admin Account
Start the container and check logs.
docker compose up -d
docker compose logs -f vaultwarden
Wait a few seconds until you see Running in the logs. Then, navigate to http://your-server-ip:8080 (if you didn't bind to localhost) or https://vault.yourdomain.com (if you already have a reverse proxy). Create your admin account by clicking "Create Account".
Important: After creating your account, immediately stop signups:
- Edit
docker-compose.ymland setSIGNUPS_ALLOWED: "false". - Restart:
docker compose up -d.
Step 4: Configure Admin Panel (Optional)
Vaultwarden has an admin panel at /admin. Access it by navigating to https://vault.yourdomain.com/admin (or http://ip:8080/admin). You'll be prompted for your ADMIN_TOKEN. Once inside, you can manage users, view diagnostics, and adjust settings. For most homelab setups, you can leave defaults, but consider:
- Disabling registration completely (if you already have your account).
- Setting up invitation-only signups if you have family members.
- Enabling 2FA enforcement for all users (highly recommended).
Step 5: Set Up Reverse Proxy (Nginx Proxy Manager)
A reverse proxy provides SSL termination, hides the backend port, and allows you to use a clean domain. We'll use Nginx Proxy Manager (NPM) for its GUI simplicity. If you prefer Caddy, skip to the alternative below.
First, create a docker-compose.yml for NPM in a separate directory (e.g., ~/npm):
version: '3.8'
services:
npm:
image: jc21/nginx-proxy-manager:2.10.3
container_name: npm
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "81:81" # Admin panel
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
Run docker compose up -d in that directory. Access NPM admin at http://your-server-ip:81, default login admin@example.com / changeme. Change the password immediately.
Now, add a proxy host:
- Go to Hosts > Proxy Hosts and click Add Proxy Host.
- Domain:
vault.yourdomain.com - Forward Hostname/IP:
vaultwarden(if NPM is on the same Docker network) orlocalhost(if not). - Forward Port:
8080(since we mapped Vaultwarden to host port 8080). - Enable Websockets Support (for real-time sync).
- Under SSL, request a new certificate with Let's Encrypt, enable Force SSL and HTTP/2.
Save and test by visiting https://vault.yourdomain.com. You should see the Vaultwarden login page.
Alternative: Caddy (simpler, one-file config)
If you prefer Caddy, create a Caddyfile:
{
email your-email@example.com
}
vault.yourdomain.com {
reverse_proxy 127.0.0.1:8080
encode zstd gzip
}
Run Caddy with Docker:
docker run -d --name caddy -p 80:80 -p 443:443 -v $PWD/Caddyfile:/etc/caddy/Caddyfile -v caddy_data:/data caddy:2.7.6
Caddy automatically obtains and renews SSL certificates. This is even simpler than NPM.
Step 6: Enable HTTPS for Vaultwarden (if not using reverse proxy)
If you're not using a reverse proxy and want direct HTTPS, you can enable built-in HTTPS in Vaultwarden by setting ROCKET_TLS environment variable. However, this is less flexible. We strongly recommend using a reverse proxy for production.
Step 7: Configure SMTP (Email Verification & Notifications)
For password reset emails and invitation emails, SMTP is required. Use your email provider's SMTP settings. For Gmail, you need an app password (not your regular password). For a privacy-friendly option, use a transactional email service like Mailgun or your own mail server (if you have one).
In your docker-compose.yml, ensure the SMTP variables are set correctly. Restart after changes.
Step 8: Backup Strategy (Automated)
Backups are critical. Vaultwarden stores all data in SQLite (or PostgreSQL) and an attachments folder. We'll create a simple cron job that dumps the database and copies the data directory to a backup location.
First, create a backup script ~/vaultwarden/backup.sh:
#!/bin/bash
BACKUP_DIR=~/vaultwarden/backups
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
# Dump SQLite database (if using SQLite)
sqlite3 ~/vaultwarden/data/db.sqlite3 ".backup '$BACKUP_DIR/db_$TIMESTAMP.sqlite3'"
# Copy entire data directory (for attachments and config)
cp -r ~/vaultwarden/data $BACKUP_DIR/data_$TIMESTAMP
# Keep only last 7 backups
find $BACKUP_DIR -type f -mtime +7 -exec rm {} \;
find $BACKUP_DIR -type d -mtime +7 -exec rm -rf {} \;
Make it executable: chmod +x backup.sh. Then add a cron job:
crontab -e
# Add this line to run daily at 2 AM
0 2 * * * /home/youruser/vaultwarden/backup.sh
For PostgreSQL, use pg_dump instead. The script above works for SQLite.
Step 9: Security Hardening (Firewall & Fail2ban)
Even behind a reverse proxy, you should secure the host. Enable UFW firewall:
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw allow 81/tcp # NPM admin (if using)
sudo ufw enable
For fail2ban, install and configure it to watch logs from NPM or Caddy. Example for NPM:
sudo apt install fail2ban -y
sudo nano /etc/fail2ban/jail.local
Add:
[nginx-proxy-manager]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 5
bantime = 3600
Restart fail2ban: sudo systemctl restart fail2ban.
Step 10: Update Vaultwarden Regularly
Vaultwarden releases frequent updates with security fixes. To update:
cd ~/vaultwarden
docker compose pull
docker compose up -d --remove-orphans
Check the GitHub releases page for breaking changes. Always backup before updating.
Advanced Configuration & Optimization
Using PostgreSQL Instead of SQLite
For high concurrency or larger teams, PostgreSQL is recommended. Add a postgres service to your docker-compose.yml:
postgres:
image: postgres:15.4
restart: unless-stopped
environment:
POSTGRES_DB: vaultwarden
POSTGRES_USER: vaultwarden
POSTGRES_PASSWORD: strongpassword
volumes:
- ./pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "vaultwarden"]
interval: 10s
timeout: 5s
retries: 5
Then set DATABASE_URL to postgresql://vaultwarden:strongpassword@postgres:5432/vaultwarden. Also add depends_on: postgres to the vaultwarden service.
Offline Mode & Icon Service
By default, Vaultwarden fetches website icons from external services. To avoid leaking your browsing habits, you can set ICON_SERVICE: "internal" (already in our config) to fetch icons directly from the target site. This is slower but more private.
Backup to Offsite (Rclone)
For disaster recovery, sync backups to a cloud storage using Rclone. Example:
rclone sync ~/vaultwarden/backups remote:backups/vaultwarden --create-empty-src-dirs
Add this to your cron script.
Two-Factor Authentication (2FA) Enforcement
In the admin panel, you can enable "Require Two-Factor Authentication" for all users. This adds a huge layer of security. Users can set up TOTP (Google Authenticator) or Duo.
Troubleshooting Common Issues
Here's a table of common problems and their solutions.
| Error / Symptom | Likely Cause | Solution |
|---|---|---|
502 Bad Gateway from reverse proxy |
Vaultwarden container not running or wrong port | Check docker compose ps; ensure port mapping is 127.0.0.1:8080:80 and proxy forwards to localhost:8080. |
403 Forbidden when accessing admin panel |
Wrong ADMIN_TOKEN or not set |
Verify the token in docker-compose.yml; restart container. |
| Cannot create account (signup disabled) | SIGNUPS_ALLOWED set to false |
Temporarily set to true, create account, then revert. |
| WebSocket connection fails (client sync errors) | Reverse proxy not configured for WebSockets | In NPM, enable Websockets Support; in Caddy, the reverse_proxy directive handles it automatically. |
Backup script fails due to sqlite3 not installed |
Missing SQLite CLI | Install with sudo apt install sqlite3. |
| Email not sent | Wrong SMTP settings or port blocked | Test SMTP credentials with swaks or telnet; ensure port 587/465 is open. |
| High memory usage | Logs growing unbounded | Configure log rotation in Docker Compose: add logging: driver: json-file, options: {max-size: "10m", max-file: "3"}. |
| Container keeps restarting | Corrupted database or permission issues | Check docker compose logs; ensure ./data has correct ownership (chown -R 1000:1000 data). |
| Cannot access Vaultwarden from other devices | Firewall or port binding | Ensure UFW allows port 8080 (if not using proxy) or 443; if bound to 127.0.0.1, only accessible locally. |
Conclusion & FAQ
Self-hosting Vaultwarden is a rewarding project that not only gives you a private password manager but also deepens your understanding of Docker, reverse proxies, and security. You've learned how to deploy it with Docker Compose, secure it with a reverse proxy and SSL, automate backups, and troubleshoot common issues. The result is a resilient, zero-cost solution that puts you in control of your credentials.
Remember to keep your system updated, monitor logs, and periodically test your backups. With Vaultwarden, you're not just using a password manager—you're building a piece of your digital sovereignty.
FAQ
1. Can I use the official Bitwarden apps with Vaultwarden?
Yes, Vaultwarden is API-compatible with all official Bitwarden clients. Simply set your server URL to your Vaultwarden domain (e.g., https://vault.yourdomain.com) in the app settings. All features like sync, 2FA, and password generator work seamlessly.
2. What's the difference between Vaultwarden and the official Bitwarden server? The official server is heavy (requires .NET, MSSQL, ~2 GB RAM) and designed for enterprise. Vaultwarden is a lightweight Rust implementation, using SQLite or PostgreSQL, consuming under 100 MB RAM. It's ideal for small teams or personal use, and it's free without premium features.
3. How do I migrate from Bitwarden cloud to Vaultwarden? Export your vault from Bitwarden as a JSON or CSV file, then import it in Vaultwarden via the web interface (Settings > Import Data). Note that attachments and file attachments are not included in CSV export; use JSON for full fidelity.
4. Is it safe to expose Vaultwarden to the internet? Yes, if you follow security best practices: use HTTPS, disable signups after initial setup, enforce 2FA, keep the server updated, and maybe add fail2ban. However, for maximum security, you can also access it only via VPN (e.g., WireGuard) and skip exposing it publicly.
5. How often should I update Vaultwarden?
Check the GitHub releases page monthly. Security patches are frequent. Before updating, always backup your data. Use docker compose pull && docker compose up -d to update. If you use PostgreSQL, ensure the version is compatible with the new release.