Tutorials

Vaultwarden: Going Deeper

Chapter 11's password vault deployed step by step: the exact proxy settings, a hashed admin token that doesn't sit in plain text, two-factor, and the extra backup that doesn't depend on your server at all.

beginner ⏱ 45 min Be Your Own Cloud · Chapter 11 vaultwardenbitwardenpasswords

· last updated 29 October 2026

Chapter 11 of Be Your Own Cloud deploys Vaultwarden on the proxy network from Chapter 8, with no port published on the host at all, and explains why. This page walks the same deploy click by click and adds what comes after: a safer admin token, two-factor on your own account, and a backup you can open even if the server is gone.

You need Chapter 8’s reverse proxy and certificate working (Reverse Proxy and HTTPS), and Chapter 10’s Pi-hole answering for vault.home.yourdomain.com.

Deploy it

mkdir -p /opt/stacks/vaultwarden
cd /opt/stacks/vaultwarden

The compose file:

File /opt/stacks/vaultwarden/docker-compose.yml
services:
  vaultwarden:
    image: vaultwarden/server:1.37.3
    container_name: vaultwarden
    restart: unless-stopped
    volumes:
      - ./data:/data
    environment:
      # set in .env: true now, false once your account exists
      - SIGNUPS_ALLOWED=${SIGNUPS_ALLOWED}
      - ADMIN_TOKEN=${ADMIN_TOKEN}
    networks:
      # Chapter 8's shared network -- no host port published at all
      - proxy

networks:
  proxy:
    external: true
Exactly as printed in Be Your Own Cloud, Chapter 11 , p. 52 · all of Chapter 11's code

Then the admin token and the .env that holds it:

Run Terminal, on the server
openssl rand -base64 48   # your ADMIN_TOKEN value, per Chapter 9
File /opt/stacks/vaultwarden/.env
# .env
ADMIN_TOKEN=the_value_from_the_line_above
SIGNUPS_ALLOWED=true
Exactly as printed in Be Your Own Cloud, Chapter 11 , p. 52 · all of Chapter 11's code

Lock the file down (Chapter 9) and start it:

chmod 600 .env
docker compose up -d
docker compose logs -f

Nothing is published on the host, so there’s no port to visit yet. That’s the point: the only way in is through the proxy.

The proxy host

In Nginx Proxy Manager, Hosts → Proxy Hosts → Add Proxy Host:

TabSettingValue
DetailsDomain Namesvault.home.yourdomain.com
DetailsSchemehttp
DetailsForward Hostname / IPvaultwarden
DetailsForward Port80
DetailsWebsockets Supporton — live sync between devices uses it
DetailsBlock Common Exploitson
SSLSSL Certificateyour DNS-challenge certificate from Chapter 8
SSLForce SSL, HTTP/2 Supporton

vaultwarden works as a hostname because both containers are on the proxy network — Docker resolves container names there.

Check it from any machine at home:

curl https://vault.home.yourdomain.com/alive

A timestamp back means the whole chain works: Pi-hole’s record, the proxy, the certificate and the container.

Create your account, then close the door

Open https://vault.home.yourdomain.com, create your account, then switch sign-ups off:

Add to /opt/stacks/vaultwarden/.env

Change this one line, then docker compose up -d.

# In .env, then docker compose up -d again:
SIGNUPS_ALLOWED=false
Exactly as printed in Be Your Own Cloud, Chapter 11 , p. 53 · all of Chapter 11's code
docker compose up -d
docker exec vaultwarden printenv SIGNUPS_ALLOWED    # should print false

Hash the admin token

The book stores ADMIN_TOKEN as a long random string. Vaultwarden accepts that, but logs a notice on every start that a plain-text token is insecure — anyone who reads .env or the container’s environment has the admin password. Vaultwarden can store a hash of it instead:

docker exec -it vaultwarden /vaultwarden hash --preset owasp

It asks for a password twice — type the token you want to log in with — and prints a line starting ADMIN_TOKEN='$argon2id$. Replace the ADMIN_TOKEN= line in .env with that whole line, single quotes included, then docker compose up -d.

The quotes matter. The hash is full of $ signs, and without single quotes Compose treats each $… as a variable to fill in, silently mangling the hash so the admin page never accepts your password.

Two-factor on your own account

In the web vault: Settings → Security → Two-step login. An authenticator app (TOTP) is the simplest. Once it’s on, the same page has View recovery code — write the code on paper and keep it somewhere that isn’t your vault. If you lose your phone, that code is the only way back into your account. (Two-Factor Codes You Won’t Lose goes further.)

Connecting the apps

In the Bitwarden browser extension, desktop app or phone app, change the server before logging in. On the login screen, the region selector (it usually says bitwarden.com) has a self-hosted option; enter https://vault.home.yourdomain.com.

The backup that doesn’t need your server

Chapter 18 backs up Vaultwarden’s database safely with SQLite’s own .backup command, and the rest of /opt/stacks/vaultwarden/data — attachments, keys — goes with /opt/stacks. That protects the server.

It’s also worth having a copy you can read without the server. In the web vault, Tools → Export, choose the file format .json (Encrypted), then the export type Password protected, and set a file password. Keep the file somewhere offline, and the password in your head or on paper.

The export type matters. Account restricted — the other choice — can only be imported back into the same account it came from, which is exactly what you won’t have if the server is gone. A password-protected export can be imported into any Bitwarden account, so if the server and its backups were both lost, your passwords wouldn’t be.

Optional: telling Vaultwarden its own address

Vaultwarden works without knowing its public address, but a few features need it — security keys and passkeys as a second factor, for one. If you want those, add to .env:

DOMAIN=https://vault.home.yourdomain.com

and one line under environment: in the compose file, next to the others:

      - DOMAIN=${DOMAIN}

Updating

Bump the tag in the compose file (vaultwarden/server:1.37.3 today), after reading the release notes and with last night’s backup behind you — Chapter 27’s routine. Your Bitwarden apps don’t need to change; they speak to whatever version the server runs.

Official documentation

Stuck on a step? Get in touch.