Your passwords are the keys to your entire digital life — email, banking, servers, cloud infrastructure, client accounts. When you store them in a SaaS password manager like LastPass or 1Password, you're trusting a third party with the most sensitive data you own. LastPass proved how badly that trust can be violated when attackers stole encrypted customer vaults in 2022. Even if encryption holds, the metadata — which sites you use, when you log in, how many credentials you have — is visible to the provider. For teams managing infrastructure, API keys, database credentials, and SSH keys, the stakes are even higher.
Self-hosting your password manager eliminates these risks entirely. Your encrypted vault lives on your server, under your control. No third party has access to your data, your metadata, or your encryption keys. And with Vaultwarden — a lightweight, community-built implementation of the Bitwarden API — you get the full Bitwarden experience (browser extensions, mobile apps, organizational vaults) without the resource overhead of the official Bitwarden server stack.
MassiveGRID Ubuntu VPS includes: Ubuntu 24.04 LTS pre-installed · Proxmox HA cluster with automatic failover · Ceph 3x replicated NVMe storage · Independent CPU/RAM/storage scaling · 12 Tbps DDoS protection · 4 global datacenter locations · 100% uptime SLA · 24/7 human support rated 9.5/10
Deploy a self-managed VPS — from $1.99/mo
Need dedicated resources? — from $19.80/mo
Want fully managed hosting? — we handle everything
Vaultwarden vs Official Bitwarden Server
Bitwarden's official self-hosted server requires multiple Docker containers (web vault, API, identity server, SQL Server, Nginx, and more), consuming 2+ GB of RAM at idle. It's designed for enterprise deployments with hundreds of users.
Vaultwarden is a complete reimplementation of the Bitwarden server API written in Rust. It runs as a single container using around 30-50 MB of RAM and includes features that Bitwarden reserves for paid plans:
| Feature | Vaultwarden (Free) | Bitwarden Official (Free) | Bitwarden Official (Paid) |
|---|---|---|---|
| Organizations | Unlimited | 2 users max | Unlimited ($4/user/mo) |
| TOTP authenticator | Yes | No | Yes ($10/yr) |
| File attachments | Yes | No | Yes ($10/yr) |
| Emergency access | Yes | No | Yes ($10/yr) |
| WebAuthn/U2F 2FA | Yes | No | Yes ($10/yr) |
| RAM usage (idle) | ~30-50 MB | ~2+ GB | ~2+ GB |
| Containers required | 1 | 8+ | 8+ |
| Database | SQLite (default) or PostgreSQL/MySQL | SQL Server | SQL Server |
Vaultwarden is fully compatible with all official Bitwarden client apps — the browser extension, desktop app, mobile apps, and CLI all work seamlessly because Vaultwarden implements the same API.
Prerequisites
You need:
- An Ubuntu VPS — Vaultwarden is extremely lightweight, so a Cloud VPS with 1 vCPU / 1GB RAM is more than enough. But infrastructure quality matters more here than for any other service. Your password manager must be available 24/7 and your data must never be lost.
- Docker and Docker Compose installed — see our Docker installation guide
- A domain name (e.g.,
vault.yourdomain.com) with a DNS A record pointing to your VPS - Nginx for reverse proxying — HTTPS is mandatory. The Bitwarden clients refuse to connect over plain HTTP. See our Nginx reverse proxy guide
Docker Compose Setup
Create the directory structure:
sudo mkdir -p /opt/vaultwarden
cd /opt/vaultwarden
Generate an admin token. This secures the Vaultwarden admin panel:
# Generate an argon2 hash for the admin token
# First, choose a strong admin password and hash it:
sudo docker run --rm -it vaultwarden/server /vaultwarden hash --preset owasp
The command will prompt you for a password and output an Argon2 hash. Save both the password (you'll use it to log in) and the hash (you'll put it in the config). Alternatively, you can use a simple plaintext token for initial setup:
# Generate a random token
openssl rand -base64 48
Create the Docker Compose file:
sudo nano /opt/vaultwarden/docker-compose.yml
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
ports:
- "127.0.0.1:8080:80"
- "127.0.0.1:3012:3012"
volumes:
- vaultwarden-data:/data
environment:
DOMAIN: "https://vault.yourdomain.com"
ADMIN_TOKEN: "your_argon2_hash_or_plaintext_token"
SIGNUPS_ALLOWED: "true"
INVITATIONS_ALLOWED: "true"
SHOW_PASSWORD_HINT: "false"
WEBSOCKET_ENABLED: "true"
LOG_FILE: "/data/vaultwarden.log"
LOG_LEVEL: "warn"
SENDS_ALLOWED: "true"
EMERGENCY_ACCESS_ALLOWED: "true"
ORG_CREATION_USERS: "all"
IP_HEADER: "X-Real-IP"
volumes:
vaultwarden-data:
Important: We bind ports to
127.0.0.1only, ensuring Vaultwarden is accessible exclusively through Nginx. Port 80 is the web vault, and port 3012 handles WebSocket connections for real-time sync between clients.
Start the container:
cd /opt/vaultwarden
sudo docker compose up -d
Verify it's running:
sudo docker compose ps
curl -s http://127.0.0.1:8080/alive
Nginx Reverse Proxy with HTTPS
HTTPS is not optional for Vaultwarden — Bitwarden clients will refuse to communicate over unencrypted connections. This is a security feature, not a limitation.
Create the Nginx configuration:
sudo nano /etc/nginx/sites-available/vault.yourdomain.com
server {
listen 80;
server_name vault.yourdomain.com;
# Allow large file uploads (for attachments)
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:3012;
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;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /notifications/hub/negotiate {
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;
}
}
Enable and test:
sudo ln -s /etc/nginx/sites-available/vault.yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Obtain the SSL certificate:
sudo certbot --nginx -d vault.yourdomain.com
For a detailed walkthrough of SSL configuration, see our Let's Encrypt SSL guide.
After Certbot completes, verify HTTPS works:
curl -I https://vault.yourdomain.com
You should receive a 200 response. Open the URL in your browser to see the Bitwarden web vault login page.
Initial Setup and Admin Panel Configuration
Access the admin panel at https://vault.yourdomain.com/admin. Enter the admin token you set in the Docker Compose file (the plaintext password, not the hash).
The admin panel lets you manage:
- General settings: Domain, organization policies, attachment limits
- User management: View registered users, delete accounts, invite users
- SMTP settings: Configure email for invitations and 2FA recovery
- Diagnostics: View server health and configuration
Configure SMTP so Vaultwarden can send invitation and verification emails. In the admin panel under SMTP Email Settings, or add these to your Docker Compose environment:
SMTP_HOST: "smtp.yourmailprovider.com"
SMTP_FROM: "vault@yourdomain.com"
SMTP_FROM_NAME: "Vaultwarden"
SMTP_SECURITY: "starttls"
SMTP_PORT: 587
SMTP_USERNAME: "your_smtp_username"
SMTP_PASSWORD: "your_smtp_password"
After updating the Docker Compose file, restart:
cd /opt/vaultwarden
sudo docker compose down
sudo docker compose up -d
Now create your first user account by going to https://vault.yourdomain.com and clicking Create Account. Use a strong master password — this is the one password you need to remember, and it encrypts everything in your vault.
Disabling New Signups
Once all your team members have created their accounts, disable public registration to prevent unauthorized access. Edit the Docker Compose file:
sudo nano /opt/vaultwarden/docker-compose.yml
Change the signup setting:
SIGNUPS_ALLOWED: "false"
If you need to add users later, you can either temporarily re-enable signups or use the invitation system. To allow only invited users:
SIGNUPS_ALLOWED: "false"
INVITATIONS_ALLOWED: "true"
Apply the changes:
cd /opt/vaultwarden
sudo docker compose down
sudo docker compose up -d
You can also restrict who can create organizations:
# Only specific email addresses can create organizations
ORG_CREATION_USERS: "admin@yourdomain.com,lead@yourdomain.com"
Enabling WebAuthn/U2F for Two-Factor Authentication
Your master password alone isn't enough. Enable two-factor authentication (2FA) for every account. Vaultwarden supports multiple 2FA methods:
- TOTP (Authenticator app) — Works with any authenticator app (Google Authenticator, Authy, Aegis)
- WebAuthn/FIDO2 — Hardware security keys (YubiKey, SoloKey) or platform authenticators (fingerprint, Face ID)
- Email — One-time codes sent via email (requires SMTP configuration)
To enable 2FA on your account:
- Log in to the web vault at
https://vault.yourdomain.com - Go to Settings → Security → Two-step Login
- Choose your preferred method and follow the setup wizard
For WebAuthn with a hardware key:
- Click Manage next to FIDO2 WebAuthn
- Enter your master password
- Click Register and touch your hardware key when prompted
- Name the key (e.g., "YubiKey 5C") and save
Recovery codes: After enabling 2FA, Vaultwarden generates recovery codes. Store these in a physically secure location (printed on paper, locked in a safe). If you lose your 2FA device and don't have recovery codes, you will be permanently locked out of your vault.
For team deployments, you can enforce 2FA via an organization policy. In the admin panel, navigate to your organization settings and enable the Require two-step login policy. This forces all organization members to set up 2FA before they can access shared vaults.
Organizational Vaults for Teams
Organizations let you share credentials securely with team members. Each organization has its own vault, and you control exactly who can access what.
Create an organization:
- In the web vault, go to New Organization
- Name it (e.g., "DevOps Team" or "Company")
- Invite members by email address
Set up collections to organize shared credentials:
- Infrastructure — Server SSH keys, database passwords, cloud provider credentials
- Development — API keys, staging environment credentials, package registry tokens
- Client Projects — Per-client credential collections
- Shared Services — Company-wide accounts (domain registrar, DNS, monitoring)
Control access with user types:
| Role | Can View | Can Edit | Can Manage Users | Can Manage Collections |
|---|---|---|---|---|
| Owner | All | All | Yes | Yes |
| Admin | All | All | Yes | Yes |
| User | Assigned collections | Assigned collections | No | No |
| Manager | Assigned collections | Assigned collections | No | Assigned collections |
For example, a junior developer might have read-only access to the Infrastructure collection but full access to the Development collection.
Encrypted Backup Strategy
This is arguably the most critical backup on your entire server. If you lose your Vaultwarden data and don't have backups, everyone on your team loses access to every credential in the vault.
Vaultwarden stores everything in the /data directory inside the container (mapped to the Docker volume). The key files are:
db.sqlite3— The main database with all encrypted vault dataconfig.json— Server configurationrsa_key*— RSA key pair used for JWT tokensattachments/— Encrypted file attachmentssends/— Bitwarden Send files
Create a comprehensive backup script:
sudo nano /opt/vaultwarden/backup.sh
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/opt/vaultwarden/backups"
ENCRYPTION_KEY_FILE="/root/.vaultwarden-backup-key"
RETENTION_DAYS=90
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
VOLUME_PATH=$(docker volume inspect vaultwarden-data --format '{{ .Mountpoint }}')
mkdir -p "$BACKUP_DIR"
# Create a temporary directory for the backup
TEMP_DIR=$(mktemp -d)
trap "rm -rf $TEMP_DIR" EXIT
# Copy all Vaultwarden data
cp -r "$VOLUME_PATH"/* "$TEMP_DIR/"
# Create a SQLite backup (ensures consistency)
docker exec vaultwarden sqlite3 /data/db.sqlite3 ".backup '/data/db-backup.sqlite3'"
cp "$VOLUME_PATH/db-backup.sqlite3" "$TEMP_DIR/db.sqlite3"
docker exec vaultwarden rm /data/db-backup.sqlite3
# Create compressed archive
tar -czf "$BACKUP_DIR/vaultwarden_$TIMESTAMP.tar.gz" -C "$TEMP_DIR" .
# Encrypt the backup with GPG
if [ -f "$ENCRYPTION_KEY_FILE" ]; then
gpg --batch --yes --symmetric \
--cipher-algo AES256 \
--passphrase-file "$ENCRYPTION_KEY_FILE" \
"$BACKUP_DIR/vaultwarden_$TIMESTAMP.tar.gz"
rm "$BACKUP_DIR/vaultwarden_$TIMESTAMP.tar.gz"
echo "Encrypted backup: vaultwarden_$TIMESTAMP.tar.gz.gpg"
else
echo "WARNING: No encryption key found. Backup is NOT encrypted."
echo "Create key: openssl rand -base64 32 > $ENCRYPTION_KEY_FILE"
fi
# Remove old backups
find "$BACKUP_DIR" -name "vaultwarden_*" -mtime +$RETENTION_DAYS -delete
echo "Backup completed at $(date)"
Set up the encryption key:
openssl rand -base64 32 | sudo tee /root/.vaultwarden-backup-key
sudo chmod 600 /root/.vaultwarden-backup-key
Store this encryption key somewhere safe outside your server. Print it and put it in a physical safe, or store it in a separate secure location. Without this key, your encrypted backups are useless. Without backups, a server failure means total credential loss.
Make the script executable and schedule it:
sudo chmod +x /opt/vaultwarden/backup.sh
# Run daily at 2 AM
sudo crontab -e
Add:
0 2 * * * /opt/vaultwarden/backup.sh >> /var/log/vaultwarden-backup.log 2>&1
For offsite backup, copy the encrypted backup to a remote location:
# Add to the backup script or as a separate cron job
rsync -az /opt/vaultwarden/backups/ user@backup-server:/backups/vaultwarden/
For a comprehensive backup strategy covering all services on your server, see our automatic backups guide.
To restore from a backup:
# Stop Vaultwarden
cd /opt/vaultwarden
sudo docker compose down
# Decrypt the backup
gpg --batch --passphrase-file /root/.vaultwarden-backup-key \
-d backups/vaultwarden_20260228_020000.tar.gz.gpg > /tmp/vw-restore.tar.gz
# Get the volume path
VOLUME_PATH=$(docker volume inspect vaultwarden-data --format '{{ .Mountpoint }}')
# Restore
sudo tar -xzf /tmp/vw-restore.tar.gz -C "$VOLUME_PATH"
# Restart
sudo docker compose up -d
# Clean up
rm /tmp/vw-restore.tar.gz
Emergency Access Configuration
Emergency access is a Bitwarden premium feature that Vaultwarden includes for free. It lets you designate trusted contacts who can request access to your vault if you become incapacitated — with a configurable waiting period during which you can deny the request.
To configure emergency access:
- In the web vault, go to Settings → Emergency Access
- Click Add Emergency Contact
- Enter the contact's email (they must have a Vaultwarden account on the same server)
- Choose the access level: View (read-only) or Takeover (full control)
- Set the Wait time (1 to 30 days) — the period after they request access before it's granted
How the process works:
- Your emergency contact requests access
- You receive an email notification
- If you don't deny the request within the wait period, access is automatically granted
- If you're available, you can approve immediately or deny the request
This is essential for small teams. If the only person who knows the admin credentials is unavailable, the team can still access shared credentials through the emergency access process.
Mobile and Browser Extension Setup
Configure all official Bitwarden clients to connect to your self-hosted instance.
Browser Extension (Chrome, Firefox, Edge, Safari)
- Install the Bitwarden extension from your browser's extension store
- Before logging in, click the gear icon (settings) on the login screen
- Under Self-hosted Environment, enter your server URL:
https://vault.yourdomain.com - Click Save and log in with your credentials
Mobile Apps (iOS, Android)
- Install the Bitwarden app from the App Store or Google Play
- On the login screen, tap the gear icon
- Enter your server URL:
https://vault.yourdomain.com - Save and log in
Desktop App (Windows, macOS, Linux)
- Download the Bitwarden desktop app
- Before logging in, click the gear icon and enter your server URL
- Log in with your credentials
CLI
The Bitwarden CLI is useful for automation and scripting:
# Install the CLI
npm install -g @bitwarden/cli
# Configure the server URL
bw config server https://vault.yourdomain.com
# Log in
bw login your@email.com
# List items
bw list items
# Get a specific password
bw get password "SSH Key - Production Server"
# Generate a password
bw generate -ulns --length 32
Hardening Vaultwarden Security
Beyond the basics, apply these security hardening measures:
Rate Limiting with Fail2Ban
Protect against brute-force attacks on your vault. First, ensure Vaultwarden logs failed login attempts by verifying LOG_FILE and LOG_LEVEL are set in your Docker Compose configuration.
Create a Fail2Ban filter:
sudo nano /etc/fail2ban/filter.d/vaultwarden.conf
[INCLUDES]
before = common.conf
[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
ignoreregex =
Create the jail:
sudo nano /etc/fail2ban/jail.d/vaultwarden.local
[vaultwarden]
enabled = true
port = 80,443
filter = vaultwarden
action = %(action_mwl)s
logpath = /opt/vaultwarden/data/vaultwarden.log
maxretry = 5
bantime = 3600
findtime = 600
Note: You may need to adjust the logpath to match the actual location where the Docker volume stores the log file. Check with:
VOLUME_PATH=$(docker volume inspect vaultwarden-data --format '{{ .Mountpoint }}')
ls -la "$VOLUME_PATH/vaultwarden.log"
Update the jail logpath accordingly, then restart Fail2Ban:
sudo systemctl restart fail2ban
sudo fail2ban-client status vaultwarden
For more Fail2Ban configurations, see our Fail2Ban advanced configuration guide.
Restrict Admin Panel Access
Limit admin panel access to specific IP addresses in Nginx:
location /admin {
allow 203.0.113.50; # Your office IP
allow 198.51.100.25; # Your home IP
deny all;
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;
}
Alternatively, disable the admin panel entirely after initial configuration by removing the ADMIN_TOKEN environment variable.
Your Vault Needs 100% Uptime
A password manager is unique among self-hosted services. If your analytics go down for an hour, you lose some data points. If your blog goes down, visitors see an error page. But if your password manager goes down, your entire team gets locked out of every system they manage.
This is why infrastructure quality matters more for Vaultwarden than almost any other self-hosted application. MassiveGRID's Cloud VPS runs on Proxmox HA clusters with automatic failover — if a physical node fails, your VM migrates to a healthy node automatically. Combined with Ceph 3x replicated NVMe storage, your vault data is stored redundantly across multiple physical drives.
Running Vaultwarden alongside other services? If running Vaultwarden alongside other services, dedicated resources ensure your password manager's response time isn't impacted by other workloads. Your vault should never compete for CPU or memory with a runaway process on the same server.
Updating Vaultwarden
Stay current with security patches:
# Always backup before updating
sudo /opt/vaultwarden/backup.sh
# Pull the latest image
cd /opt/vaultwarden
sudo docker compose pull
# Restart with the new version
sudo docker compose down
sudo docker compose up -d
# Verify the new version
sudo docker compose logs vaultwarden | head -20
Subscribe to Vaultwarden's GitHub releases to get notified of security updates. For password infrastructure, never skip security patches.
Prefer managed security infrastructure? If you want the peace of mind of enterprise-grade infrastructure without managing it yourself, MassiveGRID's managed dedicated cloud servers include proactive monitoring, automatic updates, and 24/7 incident response — ideal for mission-critical services like password management.
Final Thoughts
Self-hosting Vaultwarden gives your team a password manager with no SaaS dependency, no per-user fees, and complete data sovereignty. Every credential is encrypted on your terms, stored on your infrastructure, and accessible only to people you authorize.
The setup is straightforward — a single Docker container, an Nginx reverse proxy, and a solid backup strategy. But don't underestimate the importance of the infrastructure underneath. Your password vault is the one service that absolutely must be available 24/7 and backed up rigorously. A MassiveGRID Cloud VPS with HA failover and replicated storage provides the reliability foundation that a critical service like this demands.
Complement your Vaultwarden deployment with our guides on server security hardening, monitoring setup, and WireGuard VPN for additional layers of protection around your most sensitive infrastructure.