Vaultwarden is an open-source, Rust implementation of the Bitwarden server API. It works with the official Bitwarden browser extensions, desktop and mobile apps, and runs in a single container that needs a few dozen megabytes of RAM, which makes it a good fit for individuals, families and small teams. In this tutorial you will run Vaultwarden with Docker Compose on Ubuntu 24.04, publish it over HTTPS with Nginx and Let's Encrypt, protect the admin panel with a hashed token, and schedule daily backups.

Prerequisites

To follow this guide you need:

  • A server running Ubuntu 24.04 LTS with at least 1 GB of RAM, for example a CubePath VPS.
  • A non-root user with sudo privileges.
  • A domain name, referred to as your_domain (for example vault.example.com), with a DNS A record pointing to your_server_ip. The Bitwarden clients and web vault only work over HTTPS, so a valid certificate is mandatory.
  • Optionally, an SMTP account so Vaultwarden can send invitations and email verification.

Step 1 - Installing Docker Engine and Docker Compose

Install Docker Engine and the Compose plugin from Docker's official repository. Add the repository key:

sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

Add the repository and install the packages:

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Verify the installation:

sudo docker compose version
Docker Compose version v2.x.x

Step 2 - Generating a hashed admin token

Vaultwarden has an admin panel at /admin for managing users and settings, protected by the ADMIN_TOKEN value. Instead of storing that token in plain text, store an Argon2 hash of it. The Vaultwarden image includes a helper that creates the hash:

sudo docker run --rm -it vaultwarden/server:latest /vaultwarden hash

Enter a long, random password twice. This password is what you will type to log in to /admin. The command prints a line like this:

ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$...'

Copy the value between the single quotes. In a Compose file a $ starts a variable, so every $ in the hash must be written as $$. You can convert it with sed; paste the hash between the single quotes:

echo '$argon2id$v=19$m=65540,t=3,p=4$...' | sed 's/\$/$$/g'

Keep the converted output for the next step.

Step 3 - Creating the Docker Compose file

Create a directory for Vaultwarden and open a Compose file:

sudo mkdir -p /opt/vaultwarden
sudo nano /opt/vaultwarden/compose.yml

Add the service, replacing your_domain and the admin token:

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    environment:
      DOMAIN: "https://your_domain"
      SIGNUPS_ALLOWED: "true"
      INVITATIONS_ALLOWED: "true"
      SHOW_PASSWORD_HINT: "false"
      ADMIN_TOKEN: "$$argon2id$$v=19$$m=65540,t=3,p=4$$..."
    volumes:
      - ./data:/data
    ports:
      - "127.0.0.1:8080:80"

What these settings do:

  • DOMAIN is the public URL. Vaultwarden uses it for links in emails and for WebAuthn/passkey login, so it must match exactly.
  • SIGNUPS_ALLOWED is true only until you create your own account; you will switch it off in Step 7.
  • SHOW_PASSWORD_HINT: "false" stops the server from revealing password hints on the login page.
  • ./data holds the SQLite database, attachments and the server's private key.
  • The port is published on 127.0.0.1 only. Docker's published ports bypass UFW, so this is what keeps Vaultwarden reachable only through Nginx.

Since Vaultwarden 1.29, WebSocket notifications (live sync between clients) are served on the same port, so no second port is needed.

Restrict the file, because it contains the admin token hash:

sudo chmod 600 /opt/vaultwarden/compose.yml

Step 4 - Starting Vaultwarden

Start the container:

cd /opt/vaultwarden
sudo docker compose up -d

Check that it is running and healthy:

sudo docker compose ps
NAME          IMAGE                       STATUS
vaultwarden   vaultwarden/server:latest   Up 20 seconds (healthy)

Test it locally. The /alive endpoint returns the current timestamp when the server is up:

curl http://127.0.0.1:8080/alive

If the container is not healthy, read the log with sudo docker compose logs vaultwarden.

Step 5 - Configuring Nginx as a reverse proxy

Install Nginx and open the firewall for SSH, HTTP and HTTPS:

sudo apt install nginx
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

Create a server block:

sudo nano /etc/nginx/sites-available/vaultwarden
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      "";
}

server {
    listen 80;
    listen [::]:80;
    server_name your_domain;

    client_max_body_size 525M;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        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;
    }
}

The Upgrade and Connection headers let WebSocket notifications through, and client_max_body_size allows large attachments and Send files. X-Real-IP gives Vaultwarden the client's address, which appears in its logs and in failed login warnings.

Enable the site and reload Nginx:

sudo ln -s /etc/nginx/sites-available/vaultwarden /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Step 6 - Enabling HTTPS with Let's Encrypt

Install Certbot with the Nginx plugin and request a certificate. Certbot adds the TLS configuration and an HTTP to HTTPS redirect:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d your_domain

Verify that automatic renewal works:

sudo certbot renew --dry-run

Open https://your_domain in your browser. The Bitwarden-style web vault should load with a valid certificate.

Step 7 - Creating your account and closing sign-ups

In the web vault, click Create account, enter your email and a strong master password, and log in. The master password encrypts the vault on your device; the server never sees it and cannot recover it.

Now disable open registration. Edit the Compose file:

sudo nano /opt/vaultwarden/compose.yml

Change the value:

      SIGNUPS_ALLOWED: "false"

Recreate the container:

cd /opt/vaultwarden
sudo docker compose up -d

With sign-ups closed, other people can only join when invited to an organization or from the admin panel. Log in to https://your_domain/admin with the password you hashed in Step 2 to invite users, see registered accounts, and delete or disable them.

Step 8 - Configuring email (optional)

Invitations and email verification need SMTP. Add these variables to the environment block with your provider's values, then run sudo docker compose up -d:

      SMTP_HOST: "smtp.example.com"
      SMTP_FROM: "[email protected]"
      SMTP_PORT: "587"
      SMTP_SECURITY: "starttls"
      SMTP_USERNAME: "your_smtp_user"
      SMTP_PASSWORD: "your_smtp_password"

Use SMTP_SECURITY: "force_tls" with port 465. The admin panel has a Send test email button under SMTP Email Settings to confirm it works.

Step 9 - Backing up the vault

The data directory contains everything: db.sqlite3, attachments, Sends, config.json and the rsa_key* files that sign login tokens. Copying a live SQLite file can produce a corrupt backup, so the script copies the other files to a temporary directory and adds a consistent copy of the database made with SQLite's own backup command. Install the SQLite CLI (rsync is already present on Ubuntu Server):

sudo apt install sqlite3

Create the backup script:

sudo nano /usr/local/bin/vaultwarden-backup
#!/usr/bin/env bash
set -euo pipefail

data_dir="/opt/vaultwarden/data"
backup_dir="/var/backups/vaultwarden"
stamp="$(date +%Y%m%d-%H%M%S)"
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT

mkdir -p "$backup_dir"
rsync -a --exclude='db.sqlite3*' "$data_dir/" "$work/"
sqlite3 "$data_dir/db.sqlite3" ".backup '$work/db.sqlite3'"
tar -czf "$backup_dir/vaultwarden-$stamp.tar.gz" -C "$work" .
chmod 600 "$backup_dir/vaultwarden-$stamp.tar.gz"
find "$backup_dir" -name 'vaultwarden-*.tar.gz' -mtime +30 -delete

Make it executable, run it once and list the result:

sudo chmod 750 /usr/local/bin/vaultwarden-backup
sudo /usr/local/bin/vaultwarden-backup
sudo tar -tzf /var/backups/vaultwarden/vaultwarden-*.tar.gz | head

The listing should include ./rsa_key.pem, ./config.json (if you saved admin settings) and ./db.sqlite3. To restore, stop the container, extract the archive into /opt/vaultwarden/data and start it again. Schedule it daily:

echo '15 3 * * * root /usr/local/bin/vaultwarden-backup' | sudo tee /etc/cron.d/vaultwarden-backup

Vault items are encrypted with each user's master password, but the backup still holds sensitive metadata and keys, so store copies on another server or in encrypted object storage.

Updating Vaultwarden

Take a backup, pull the new image and recreate the container:

cd /opt/vaultwarden
sudo /usr/local/bin/vaultwarden-backup
sudo docker compose pull
sudo docker compose up -d

Database migrations run automatically on start. Check the release notes before upgrading.

Troubleshooting

  • The web vault shows a blank page or a crypto error: you are accessing it over plain HTTP. The web vault requires HTTPS.
  • Clients do not sync changes live: the Nginx block is missing the Upgrade and Connection headers.
  • The admin panel rejects your password: you must type the plain password you entered in Step 2, not the hash. If the log warns about the token, check that every $ became $$ in compose.yml.
  • Passkey or WebAuthn login fails: DOMAIN does not exactly match the URL in the browser.

Conclusion

Vaultwarden is now running on Ubuntu 24.04 behind Nginx with HTTPS, with sign-ups closed, a hashed admin token and daily backups. Next, install the Bitwarden browser extension and mobile apps and choose Self-hosted with https://your_domain at login, enable two-step login for every account, and consider adding Fail2ban rules for failed vault and admin logins.