Self-Hosted Services and ProductivityDocs and File WorkflowsIntermediate2-3 hoursLab

Build a Vaultwarden Lab with HTTPS, Backups, and Recovery Validation

Deploy Vaultwarden behind an HTTPS reverse proxy, keep the application bound to localhost, disable open signup after bootstrap, and create a consistent backup of the vault data...

Last reviewed8/19/2026
password vaultsbackup validationfamily recovery
Docker Compose v2VaultwardenHTTPS reverse proxySQLiteLinux

Expected Outcome

A Vaultwarden Lab reachable only through HTTPS, with persistent data isolated from the container, a documented recovery artifact, and validation that distinguishes 'a backup file exists' from 'the vault can be recovered.'

Assumptions

  • A Linux host with Docker Engine and the Docker Compose plugin installed.

  • A DNS name that resolves to the reverse proxy for the Lab, or an internal DNS name with a trusted certificate path.

  • An HTTPS-capable reverse proxy such as Caddy, Nginx, or Nginx Proxy Manager.

  • A maintenance window for the short container stop used to create a consistent Lab backup.

  • A backup destination that is not inside the live Vaultwarden data directory.

Bill of Materials

  • The official Vaultwarden container image.

  • A reviewed Vaultwarden image tag or digest for the Lab.

  • A local persistent directory such as `/opt/vaultwarden/vw-data`.

  • A secure, offline place for recovery instructions and any emergency-access material.

Build Steps

  1. Verify the container runtime and create the Lab directory

    Use Compose v2 and keep the service definition, persistent data, and backups in separate paths.

    Read-only command: verify target and scope

    docker --version
    docker compose version
    sudo install -d -m 0750 /opt/vaultwarden/vw-data /opt/vaultwarden/backups
    cd /opt/vaultwarden
  2. Define Vaultwarden on a localhost-only application port

    Vaultwarden's web vault requires HTTPS for a secure browser context. Bind the application to loopback and let the reverse proxy own external HTTPS rather than exposing Vaultwarden's HTTP port directly to the LAN. Replace the example hostname and reviewed image tag before deployment.

    Changes system state: review before running

    sudo tee /opt/vaultwarden/compose.yaml > /dev/null <<'EOF'
    services:
      vaultwarden:
        image: vaultwarden/server:<reviewed-tag>
        container_name: vaultwarden
        restart: unless-stopped
        environment:
          DOMAIN: "https://vault.example.internal"
          SIGNUPS_ALLOWED: "true"
        volumes:
          - ./vw-data:/data
        ports:
          - "127.0.0.1:8000:80"
    EOF
    docker compose -f /opt/vaultwarden/compose.yaml config
  3. Start Vaultwarden and configure HTTPS at the reverse proxy

    Start the container, then configure the reverse proxy to terminate TLS for the exact DOMAIN value and forward to http://127.0.0.1:8000. Modern Vaultwarden handles notification endpoints on the main HTTP service; do not reintroduce old separate websocket-port recipes unless current project guidance for your proxy requires it.

    Changes system state: review before running

    cd /opt/vaultwarden
    docker compose up -d
    docker compose ps
    curl -I http://127.0.0.1:8000/
  4. Bootstrap the first account and close open signup

    Use the HTTPS URL to create the intended first account. After bootstrap, change SIGNUPS_ALLOWED to false, validate the Compose file, and recreate the container. This prevents the Lab from remaining in an unnecessary open-registration state.

    Changes system state: review before running

    sudo sed -i 's/SIGNUPS_ALLOWED: "true"/SIGNUPS_ALLOWED: "false"/' /opt/vaultwarden/compose.yaml
    docker compose -f /opt/vaultwarden/compose.yaml config
    docker compose -f /opt/vaultwarden/compose.yaml up -d --force-recreate
  5. Validate the HTTPS boundary

    Confirm that the public Lab URL presents the expected certificate and that the application port itself is bound only to loopback on the host.

    Manual or UI step

    • ss -ltnp | grep ':8000'

    Read-only command: verify target and scope

    curl -I https://vault.example.internal/
  6. Create a consistent stopped-container backup

    For this Lab, use a short maintenance stop so SQLite and the rest of /data are quiescent together. Archive the complete persistent directory to a separate backup path, restart Vaultwarden immediately, and record a checksum. This avoids assuming a copy of only db.sqlite3 also protects attachments, keys, and other persisted state.

    Read-only command: verify target and scope

    cd /opt/vaultwarden
    STAMP=$(date +%Y%m%d-%H%M%S)
    docker compose stop vaultwarden
    sudo tar -C /opt/vaultwarden -cpf "/opt/vaultwarden/backups/vaultwarden-$STAMP.tar" vw-data
    docker compose start vaultwarden
    sudo sha256sum "/opt/vaultwarden/backups/vaultwarden-$STAMP.tar" | sudo tee "/opt/vaultwarden/backups/vaultwarden-$STAMP.sha256"
  7. Validate the backup artifact without overwriting the live vault

    Verify the checksum and extract the archive into a disposable directory. Confirm that the SQLite database and expected persisted directories/files are present. If sqlite3 is installed on the host, run an integrity check against the extracted database rather than the live file.

    Destructive: review before running

    cd /opt/vaultwarden/backups
    sudo sha256sum -c vaultwarden-$STAMP.sha256
    sudo rm -rf /tmp/vaultwarden-restore-check && sudo mkdir -p /tmp/vaultwarden-restore-check
    sudo tar -C /tmp/vaultwarden-restore-check -xpf "vaultwarden-$STAMP.tar"
    sudo test -f /tmp/vaultwarden-restore-check/vw-data/db.sqlite3 && echo 'Database present'

    Manual or UI step

    • command -v sqlite3 >/dev/null && sudo sqlite3 /tmp/vaultwarden-restore-check/vw-data/db.sqlite3 'PRAGMA integrity_check;' || echo 'sqlite3 not installed; skip DB integrity check'

  8. Write recovery instructions outside the vault

    Document the DNS name, reverse-proxy dependency, Compose path, backup location, restore order, and who is authorized to perform recovery. Store that document in an appropriately protected offline or independent location—not as plaintext beside the live vault and not only inside Vaultwarden itself.

Validation

  • Vaultwarden is reachable through the intended HTTPS URL and the client trusts the certificate.

  • Port 8000 is bound only to `127.0.0.1` on the Vaultwarden host.

  • Open signup is disabled after the intended bootstrap account is created.

  • A stopped-container backup archive verifies by SHA-256 and contains the persistent Vaultwarden data, not just a container image.

  • The extracted backup contains the SQLite database and passes `PRAGMA integrity_check` when SQLite tooling is available.

  • Recovery instructions are stored independently of the live vault.

Troubleshooting

  • If the web vault fails in a browser while localhost HTTP works, verify the external `DOMAIN`, TLS certificate, reverse-proxy target, forwarded headers, and HTTPS access first. The Vaultwarden project explicitly requires a secure browser context for the web vault.

  • If the container restarts but data appears missing, stop and inspect the bind mount before creating new accounts. A wrong host path can make a fresh empty `/data` look like data loss.

    Read-only command: verify target and scope

    docker inspect vaultwarden --format '{{json .Mounts}}'
    sudo ls -la /opt/vaultwarden/vw-data
  • If a restore is required, stop Vaultwarden and preserve the current data directory before replacing it. Do not mix a restored SQLite database with stale WAL files from a different database generation.

Cleanup or Rollback

  • Remove `/tmp/vaultwarden-restore-check` after backup validation; it contains sensitive copied vault data.

  • For a disposable Lab, stop the stack with `docker compose down` but preserve any backup you intend to use before deleting `/opt/vaultwarden/vw-data`.

  • Do not use `docker compose down -v` or broad filesystem deletion as generic cleanup when you have not confirmed where persistent vault data lives.

Next Improvements

  • Copy encrypted backup artifacts off the Vaultwarden host and set a retention policy.

  • Perform a full restore rehearsal on an isolated host before treating the backup process as production-ready.

  • Add MFA and emergency-access practices appropriate to the people who will depend on the vault.

References

Keep Moving

Build on what you just completed

Continue with a related Lab or return to this build path for a different implementation.