Vaultwarden is a small, fast server that speaks the same language as Bitwarden, so the official Bitwarden apps and browser extensions work with it unchanged. It uses about 50 MB of RAM, which makes it one of the most useful things you can run on even the cheapest VPS. Your passwords end up on a server you control.
One rule before anything else: the Vaultwarden web vault only works over HTTPS (or on localhost), because browsers only allow its encryption code in a secure context. That's why this guide comes after Nginx Proxy Manager: you need a domain and a certificate before your phone can connect.
Prerequisites
- Docker Engine and the Compose plugin (see the Compose guide)
- Nginx Proxy Manager running from
/opt/npm - A subdomain for the vault, such as
vault.example.com, with an A record pointing at your server
1. Write the Compose file
sudo mkdir -p /opt/vaultwarden
cd /opt/vaultwarden
sudo nano compose.yml
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: "https://vault.example.com" # your real subdomain
SIGNUPS_ALLOWED: "true" # switched off in step 4
volumes:
- ./vw-data:/data
ports:
- "127.0.0.1:8222:80" # localhost only, for the tunnel
networks:
- default
- npm
networks:
npm:
external: true
name: npm_default # NPM's network, so it can reach "vaultwarden"
Everything Vaultwarden stores (the database, attachments, its keys) lives in ./vw-data, right next to the compose file. That makes the backup in step 6 a single folder.
2. Start it and create your account
sudo docker compose up -d
sudo docker compose logs -f # Ctrl-C once it says Rocket has launched
On your own computer, open a tunnel and visit the vault. localhost counts as a secure context, so this works even before HTTPS:
ssh -L 8222:localhost:8222 your-user@your-server-ip
# then open http://localhost:8222
Click Create account. Your master password is the one thing that can't be reset (not by you, not by the server), so make it long and write it down somewhere physical.
3. Put it behind HTTPS
In the Nginx Proxy Manager admin UI: Hosts → Proxy Hosts → Add.
- Domain:
vault.example.com - Forward:
http·vaultwarden· port80 - Turn on Websockets Support (live sync between devices uses it) and Block Common Exploits
- SSL tab: request a new certificate and enable Force SSL
Now open https://vault.example.com, log in, then install the Bitwarden app or browser extension. On its login screen, choose self-hosted and enter your URL.
4. Close the front door
Once you (and anyone you're hosting) have accounts, stop strangers from signing up. Change one line in compose.yml:
SIGNUPS_ALLOWED: "false"
sudo docker compose up -d # recreates the container with the new setting
5. Optional: the admin page
Vaultwarden has an admin page at /admin for inviting users and changing settings. It's off unless you set an ADMIN_TOKEN, and that token should be a hash, not a plain password:
sudo docker exec -it vaultwarden /vaultwarden hash
# type a strong password twice; it prints ADMIN_TOKEN='$argon2id$...'
Add it under environment: in compose.yml, and double every $ sign ($$argon2id$$v=19$$m=...), because Compose treats a single $ as a variable. Then run sudo docker compose up -d and log in at /admin with the password you typed (not the hash). If you don't need it, leave it disabled. An admin page that doesn't exist can't be attacked.
6. Updates and backups
Updates are one command. Vaultwarden releases often, and staying current matters for a security app:
cd /opt/vaultwarden
sudo docker compose pull && sudo docker compose up -d
Backups: the whole vault is the vw-data folder. The safest simple method is a few seconds of downtime so the SQLite database isn't mid-write:
cd /opt/vaultwarden
sudo docker compose stop
sudo tar -czf /root/backups/vaultwarden-$(date +%F).tar.gz vw-data
sudo docker compose start
Run that nightly from cron (see cron jobs for beginners) and copy the archives off the server (see Google Drive backups). A password vault that only exists on one disk is a single point of failure for your entire online life.
Troubleshooting
- "This browser requires HTTPS" or a blank vault page: you're on plain
http://with a real domain. Use the HTTPS URL, or thelocalhosttunnel. - 502 Bad Gateway from NPM: the two containers aren't on the same network.
sudo docker network inspect npm_defaultshould list bothnpmandvaultwarden. - Apps won't log in but the website works: check that
DOMAINincompose.ymlexactly matches the URL you entered in the app, includinghttps://. - Admin page says the token is invalid: a single
$got eaten by Compose. Double them all, then recreate the container.
Official documentation: Vaultwarden docs · Repository: dani-garcia/vaultwarden