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 · port 80
  • 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 the localhost tunnel.
  • 502 Bad Gateway from NPM: the two containers aren't on the same network. sudo docker network inspect npm_default should list both npm and vaultwarden.
  • Apps won't log in but the website works: check that DOMAIN in compose.yml exactly matches the URL you entered in the app, including https://.
  • 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