Bitwarden's cloud service is genuinely good. I've covered it before and the security track record holds up — independent audits, open-source client code, no breaches. For most people, using Bitwarden's hosted service is the right call.
Vaultwarden is for the tier above that.
It's an unofficial Bitwarden-compatible server, written in Rust, that you run on your own infrastructure. Your passwords live on your machine. You control the backups. There's no third-party service in the chain. The official Bitwarden server requires .NET and a SQL Server instance — it's heavy and complicated to run at home. Vaultwarden runs in a single Docker container and uses SQLite. On a $5/month VPS it uses maybe 50MB of RAM.
The tradeoff: it's not officially supported by Bitwarden, Inc. Security patches depend on the project maintainer. That said, the current lead maintainer is employed by Bitwarden and contributes on their own time — so it's not as unofficial as it sounds. Vaultwarden v1.35.0 (released early 2026) added OpenID Connect SSO, immutable releases, and release attestation, which signals a project taking security seriously.
Worth knowing about who this is for: if you travel internationally, if you work in a high-sensitivity environment, or if you're simply not comfortable with any third party holding your password vault — even an encrypted one — this is the setup for you.
Before You Start
You need:
- A VPS with Docker installed. If you haven't set yours up yet, start here.
- A domain name you control, with a DNS A record pointing to your VPS. Vaultwarden requires HTTPS — the Bitwarden clients won't connect to a plain HTTP endpoint, and several features (WebAuthn, passkeys) depend on a valid SSL context.
- Ports 80 and 443 open on the VPS firewall. Port 80 is only needed temporarily for the Let's Encrypt ACME challenge.
Install Nginx and Certbot
Vaultwarden doesn't handle SSL itself. You put Nginx in front of it as a reverse proxy.
sudo apt update && sudo apt install nginx certbot python3-certbot-nginx -y
Get your SSL certificate before touching Vaultwarden — it's easier to do it while port 80 is clean:
sudo certbot --nginx -d vault.yourdomain.com
Certbot modifies your Nginx config automatically and sets up auto-renewal via a systemd timer. After it finishes, verify the timer is active:
systemctl list-timers | grep certbot
Run Vaultwarden
Generate your admin token first. This controls access to the /admin panel:
openssl rand -base64 48
Copy the output. You'll need it in a moment.
Now run the container:
docker run -d \
--name vaultwarden \
-e DOMAIN="https://vault.yourdomain.com" \
-e SIGNUPS_ALLOWED=true \
-e ADMIN_TOKEN="<paste your token here>" \
-e WEBSOCKET_ENABLED=true \
-v /vw-data:/data \
-p 127.0.0.1:8080:80 \
-p 127.0.0.1:3012:3012 \
--restart unless-stopped \
vaultwarden/server:latest
Binding to 127.0.0.1 on both ports is intentional. Vaultwarden shouldn't be directly reachable from the internet — only through Nginx. The WEBSOCKET_ENABLED=true flag enables real-time sync (so when you add a password on your phone, it shows up on your laptop immediately).
/vw-data is where everything lives — the SQLite database, attachments, configuration. Back this up. More on that below.
Configure Nginx
Replace the default Nginx config for your domain. This is the minimal working config:
server {
listen 443 ssl;
server_name vault.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/vault.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vault.yourdomain.com/privkey.pem;
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_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
server {
listen 80;
server_name vault.yourdomain.com;
return 301 https://$host$request_uri;
}
The second server block redirects HTTP to HTTPS. The /notifications/hub location is the WebSocket endpoint for real-time sync.
Test the config and reload:
sudo nginx -t
sudo systemctl reload nginx
Visit https://vault.yourdomain.com — you should see the Bitwarden/Vaultwarden login page.
Create Your Account, Then Lock the Door
Sign up at your Vaultwarden URL just like you would on Bitwarden's site. Once your account is created, disable signups immediately:
docker stop vaultwarden
docker run -d \
--name vaultwarden \
-e DOMAIN="https://vault.yourdomain.com" \
-e SIGNUPS_ALLOWED=false \
-e ADMIN_TOKEN="<your token>" \
-e WEBSOCKET_ENABLED=true \
-v /vw-data:/data \
-p 127.0.0.1:8080:80 \
-p 127.0.0.1:3012:3012 \
--restart unless-stopped \
vaultwarden/server:latest
SIGNUPS_ALLOWED=false means anyone who finds your URL can't create an account. This is the step people forget. An open Vaultwarden registration is a problem — anyone could sign up and your server becomes someone else's password manager host.
Alternatively, manage this from the admin panel at https://vault.yourdomain.com/admin. Log in with your ADMIN_TOKEN, and you can toggle signups, invite specific users by email, and see server diagnostics.
Connecting the Bitwarden Client
The standard Bitwarden apps — browser extension, desktop app, iOS, Android — all work with Vaultwarden. No special build needed.
Change the server URL in the client before logging in:
- Browser extension: click the Vaultwarden icon → Settings → Self-hosted environment → Server URL → enter
https://vault.yourdomain.com - Mobile (iOS/Android): open the app → tap the region selector near the top → Self-hosted → Server URL
- Desktop app: same as browser extension
Then log in with the credentials you created. Everything syncs normally from there.
Backups: Don't Skip This
A self-hosted password manager with no backups is genuinely dangerous. If the server dies and you lose /vw-data, your passwords are gone.
The /vw-data directory contains everything:
db.sqlite3— your entire vault. This is the file.attachments/— any files attached to vault entriessends/— Bitwarden Send filesconfig.json— server configurationrsa_keyfiles — used for JWT signing
Minimum viable backup: copy db.sqlite3 off the server daily. A simple cron job with rsync to a different machine works. For a proper setup, back up the entire /vw-data directory.
# Example: daily rsync to a backup server
0 3 * * * rsync -az /vw-data/ user@backup-server:/backups/vaultwarden/
Also worth doing: enable your VPS provider's volume snapshots if they offer them. Weekly snapshots of the whole disk give you a clean recovery path for anything that goes wrong at the system level — not just the vault database.
Where This Sits in the Stack
If you've looked at the full password manager comparison, Vaultwarden fits at the high-control end. You trade convenience (no managed cloud, you're responsible for uptime and backups) for complete ownership. The Bitwarden clients are polished and multi-platform. The protocol is open-source and audited. And because Vaultwarden speaks the same API as Bitwarden, you're not locked into a custom ecosystem — if you ever want to migrate to Bitwarden's hosted service, you export from one and import to the other.
That's a pretty good deal for a $5 VPS and a Docker container.