Vaultwarden is a free, open-source password manager compatible with Bitwarden clients. Unlike cloud-hosted password managers, you control the server, no company between you and your encrypted vault. This guide covers complete setup: Docker deployment, reverse proxy configuration, automated backups, HTTPS, and admin panel hardening.
Table of Contents
- Why Self-Host Vaultwarden?
- Prerequisites
- Step 1 - Set Up VPS and SSH
- Step 2 - Create Vaultwarden Docker Compose
- Step 3 - Configure Reverse Proxy (Caddy)
- Step 4 - Launch Vaultwarden
- Step 5 - Access Admin Panel
- Step 6 - Add First User
- Step 7 - Enable SMTP for Email Invitations
- Step 8 - Automated Backups
- Step 9 - Enable Two-Factor Authentication (2FA)
- Step 10 - Configure Organization (Optional)
- Client Setup
- Monitoring and Maintenance
- Troubleshooting
- Security Hardening Checklist
- Cost Breakdown
- Alternatives
Why Self-Host Vaultwarden?
Advantages:
- No third-party access to encrypted data
- Full control over updates and deployment
- Cheap ($5-15/month cloud hosting)
- Works offline if you proxy locally
- Open-source code (audit it)
Tradeoffs:
- You manage backups, updates, and security
- No company customer support
- Single point of failure (if your server fails, you manage recovery)
- Small environment (community-maintained, not commercial)
Best for - Developers, privacy-conscious individuals, teams wanting complete control. Not for non-technical users.
Prerequisites
- VPS with 2GB RAM, 20GB storage (Vultr, Hetzner, DigitalOcean)
- Domain name pointing to your VPS IP
- SSH access to VPS
- Basic Linux command-line comfort
Estimated cost - $5/month hosting + $10/year domain = $70/year total
Step 1 - Set Up VPS and SSH
Using DigitalOcean as example (Ubuntu 24.04):
SSH into VPS
ssh root@your.vps.ip
Update system
apt update && apt upgrade -y
Install Docker and Docker Compose
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
Verify Docker installed
docker --version
Step 2 - Create Vaultwarden Docker Compose
Create /root/vaultwarden/docker-compose.yml:
version: '3'
services:
vaultwarden:
image: vaultwarden/server:latest
restart: always
ports:
- "80:80"
- "443:443"
environment:
DOMAIN: "https://vault.yourdomain.com"
SIGNUPS_ALLOWED: "false"
INVITATIONS_ORG_ALLOW_USER: "false"
SEND_FOLDER: "true"
EXTENDED_LOGGING: "true"
LOG_FILE: "/data/vaultwarden.log"
# Admin panel access token (generate: openssl rand -base64 48)
ADMIN_TOKEN: "your-secret-admin-token-here"
# SMTP for email invitations (optional, see below)
MAIL_ENABLED: "false"
volumes:
- ./data:/data
- ./certs:/certs
networks:
- vaultwarden-network
# Caddy reverse proxy with automatic HTTPS
caddy:
image: caddy:latest
restart: always
ports:
- "80:80"
- "443:443"
environment:
DOMAIN: "vault.yourdomain.com"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./data/caddy_data:/data
- ./data/caddy_config:/config
networks:
- vaultwarden-network
depends_on:
- vaultwarden
networks:
vaultwarden-network:
driver: bridge
Step 3 - Configure Reverse Proxy (Caddy)
Create /root/vaultwarden/Caddyfile:
vault.yourdomain.com {
encode gzip
# Reverse proxy to Vaultwarden container
reverse_proxy localhost:80 {
# Forward real IP
header_upstream X-Real-IP {http.request.remote.host}
header_upstream X-Forwarded-For {http.request.remote.host}
header_upstream X-Forwarded-Proto {http.request.scheme}
}
# Rate limiting (prevent brute force)
rate_limit 10r/s
# Security headers
header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
header X-Content-Type-Options "nosniff"
header X-Frame-Options "DENY"
header X-XSS-Protection "1; mode=block"
header Referrer-Policy "no-referrer"
header Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; font-src 'self'"
# TLS certificate auto-renewal
tls internal
}
Step 4 - Launch Vaultwarden
cd /root/vaultwarden
Generate secure admin token
openssl rand -base64 48
Update docker-compose.yml with the token you just generated
Edit ADMIN_TOKEN in docker-compose.yml
Create data directory
mkdir -p data
Start containers
docker compose up -d
Verify running
docker compose ps
docker compose logs -f vaultwarden
Expected output:
vaultwarden | [INFO] Vaultwarden is ready
vaultwarden | Rocket has launched from http://0.0.0.0:80
caddy | caddy started successfully
Step 5 - Access Admin Panel
Open browser to https://vault.yourdomain.com/admin
You’ll see login prompt. Enter your admin token (from Step 3) into the “Master Password Hash” field.
Admin panel options:
- Disable new user registrations (already set
SIGNUPS_ALLOWED: false) - View user list and delete users
- Monitor server logs
- Update configuration
- Check database stats
Disable logins to admin panel after setup:
Edit docker-compose.yml
Add environment variable:
ADMIN_DISABLE_2FA: "true"
ADMIN_SESSION_LIFETIME - 5 # Session expires after 5 minutes
Restart:
docker compose restart vaultwarden
Step 6 - Add First User
Two options:
Option A: Admin panel (easiest)
- Go to
vault.yourdomain.com/admin - Click “Users”
- Click “New User”
- Enter email, set temporary password
- Share link with user
Option B - Manual command (if admin panel fails)
docker compose exec -it vaultwarden \
/vaultwarden hash --preset owasp --input yourpassword
Step 7 - Enable SMTP for Email Invitations
Edit docker-compose.yml:
environment:
MAIL_ENABLED: "true"
MAIL_FROM: "vault@yourdomain.com"
MAIL_FROM_NAME: "Vaultwarden"
# Example: Gmail with app-specific password
MAIL_SMTP_HOST: "smtp.gmail.com"
MAIL_SMTP_SECURITY: "force_tls"
MAIL_SMTP_PORT: "587"
MAIL_SMTP_AUTH: "Login"
MAIL_SMTP_USERNAME: "your-email@gmail.com"
MAIL_SMTP_PASSWORD: "app-specific-password"
For Gmail:
- Enable 2-factor authentication on your Google Account
- Generate app password: https://myaccount.google.com/apppasswords
- Use app password above (not your regular password)
Restart:
docker compose restart vaultwarden
Step 8 - Automated Backups
Create backup script /root/vaultwarden/backup.sh:
#!/bin/bash
BACKUP_DIR="/root/vaultwarden/backups"
DB_PATH="/root/vaultwarden/data/db.sqlite3"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
Create backup directory
mkdir -p $BACKUP_DIR
Backup database
cp $DB_PATH $BACKUP_DIR/db_$TIMESTAMP.sqlite3
Backup attachments and sends
tar -czf $BACKUP_DIR/vaultwarden_$TIMESTAMP.tar.gz \
/root/vaultwarden/data/attachments \
/root/vaultwarden/data/sends \
/root/vaultwarden/data/icon_cache
Delete backups older than 30 days
find $BACKUP_DIR -mtime +30 -delete
Upload to remote storage (optional)
aws s3 cp $BACKUP_DIR/db_$TIMESTAMP.sqlite3 s3://your-bucket/backups/
Make executable and add to cron:
chmod +x /root/vaultwarden/backup.sh
Edit crontab
crontab -e
Add line (daily backup at 2am):
0 2 * * * /root/vaultwarden/backup.sh
Verify:
Run backup manually
/root/vaultwarden/backup.sh
Check results
ls -la /root/vaultwarden/backups/
Step 9 - Enable Two-Factor Authentication (2FA)
In admin panel:
- Settings → Authentication
- Set
TWOFACTORoptions:TWOFACTOR_EMAIL: Allow email-based 2FATWOFACTOR_AUTHENTICATOR: Allow authenticator app (Google Authenticator, Authy)
environment:
TWOFACTOR: "true"
TWOFACTOR_EMAIL: "true"
TWOFACTOR_AUTHENTICATOR: "true"
TWOFACTOR_REMEMBER: "true"
TWOFACTOR_REMEMBER_EXPIRES_IN: 30
Step 10 - Configure Organization (Optional)
If you want to share passwords with family/team:
- Log in to web vault:
vault.yourdomain.com - Click “New Organization”
- Name it (“Family Passwords” or “Work Team”)
- Add users via admin panel or send invitations
- Create collections (folders) and assign to users
Example org structure:
Organization - MyFamily
Collection: Banking (shared with spouse)
Collection: Streaming (shared with all)
Collection: Personal (only me)
Client Setup
Desktop (Windows, Mac, Linux):
- Download Bitwarden client (works with Vaultwarden)
- Login with your email
- Settings → Server URL →
https://vault.yourdomain.com - Restart client, re-authenticate
Mobile (iOS, Android):
- Download Bitwarden app
- Settings → Server URL →
https://vault.yourdomain.com - Login with email
- Pin app to home screen
Browser Extension:
- Install Bitwarden extension (Chrome, Firefox, Safari, Edge)
- Click icon → Settings → Server URL →
https://vault.yourdomain.com - Login once
- Auto-fill works normally
Monitoring and Maintenance
Weekly:
- Check logs:
docker compose logs vaultwarden - Look for errors (bad logins, sync failures)
Monthly:
- Verify backups exist and are fresh
- Test restore procedure (copy backup, start test container)
- Check for Vaultwarden updates:
docker compose pull docker compose up -d
Quarterly:
- Audit users (admin panel → Users)
- Remove inactive accounts
- Review security logs
Troubleshooting
Connection refused when accessing admin panel:
Check if containers running
docker compose ps
Check logs
docker compose logs caddy
docker compose logs vaultwarden
Restart all
docker compose restart
Email not sending:
Test SMTP connection
docker compose exec -it vaultwarden \
/vaultwarden test-mail your-email@gmail.com
Database locked error:
Stop containers, wait 10 seconds, restart
docker compose down
sleep 10
docker compose up -d
Forgotten admin password: You can’t reset it directly. Instead:
- Stop Vaultwarden
- Replace database with backup
- Restart
This is why backups are critical.
Security Hardening Checklist
- Changed default admin token (not empty string)
- Disabled public signup (
SIGNUPS_ALLOWED: false) - Enabled HTTPS with valid certificate
- Configured rate limiting in Caddy
- Set strong master password (20+ chars)
- Enabled 2FA for your account
- Scheduled daily backups
- Stored backups on external drive or cloud
- Reviewed firewall rules (only ports 80/443 open)
- Tested backup restoration
Cost Breakdown
Annual cost:
- VPS: $60/year (DigitalOcean $5/month)
- Domain: $10/year (Google Domains)
- Total: $70/year
vs. Bitwarden Premium:
- Bitwarden: $10/month = $120/year
- Savings: $50/year
but you pay in time:
- Initial setup: 2-3 hours
- Monthly maintenance: 1-2 hours
- Backup restore (disaster): 1-2 hours
Alternatives
Bitwarden Cloud ($10/month):
- Pro: No maintenance, free mobile apps, family sharing
- Con: Trust Bitwarden, pay subscription
1Password ($36-99/year):
- Pro: Slick UX, family plan
- Con: Proprietary, more expensive
KeePass (free, local):
- Pro: Zero cloud trust
- Con: No sync, no mobile, management burden
Frequently Asked Questions
How long does it take to set up self-hosted password manager vaultwarden?
For a straightforward setup, expect 30 minutes to 2 hours depending on your familiarity with the tools involved. Complex configurations with custom requirements may take longer. Having your credentials and environment ready before starting saves significant time.
What are the most common mistakes to avoid?
The most frequent issues are skipping prerequisite steps, using outdated package versions, and not reading error messages carefully. Follow the steps in order, verify each one works before moving on, and check the official documentation if something behaves unexpectedly.
Do I need prior experience to follow this guide?
Basic familiarity with the relevant tools and command line is helpful but not strictly required. Each step is explained with context. If you get stuck, the official documentation for each tool covers fundamentals that may fill in knowledge gaps.
Is this approach secure enough for production?
The patterns shown here follow standard practices, but production deployments need additional hardening. Add rate limiting, input validation, proper secret management, and monitoring before going live. Consider a security review if your application handles sensitive user data.
Where can I get help if I run into issues?
Start with the official documentation for each tool mentioned. Stack Overflow and GitHub Issues are good next steps for specific error messages. Community forums and Discord servers for the relevant tools often have active members who can help with setup problems.
Related Articles
- How to Self-Host Bitwarden Vaultwarden: Complete Setup Guide
- Self-Hosted Password Manager Comparison
- How To Set Up Enterprise Password Manager With Zero Knowledg
- Best Password Manager for Developers: A Technical Guide
- Bitwarden vs Vaultwarden Self-Hosted: A Technical Comparison
- Best Self-Hosted AI Model for JavaScript TypeScript Code Built by theluckystrike. More at zovo.one