🛠️ Tutorials · ⏱ 10 min read

Host Vaultwarden with Docker and HTTPS in 2026 (Complete Tutorial, Caddy Reverse Proxy)

Step-by-step 2026 guide to self-host Vaultwarden with Docker. Secure deployment, automatic HTTPS via Caddy and Let's Encrypt, protected admin panel, 2FA, encrypted backups, and hardening. Ready-to-use docker-compose and Caddyfile configs.

S By Selfhostr Team · independent tests
Host Vaultwarden with Docker and HTTPS in 2026 (Complete Tutorial, Caddy Reverse Proxy)
ⓘ This article may contain affiliate links (no extra cost to you, it supports our tests). See the disclosure.
💾
<100 MB
RAM Usage
🦀
Rust
Language
🔐
Bitwarden Clients
Compatibility

👍 What we like

  • Extremely lightweight resource footprint suitable for small VPS
  • Full compatibility with official Bitwarden applications and extensions
  • Self-hosted solution eliminates third-party cloud dependency
  • Automated HTTPS via Caddy reverse proxy with Let's Encrypt

👎 What to watch

  • Requires manual generation of Argon2 hashed admin tokens
  • Complex initial setup involving Docker, DNS, and firewall configuration
  • Admin token requires careful escaping of special characters in env files
📑 Contents

Do you want your own password manager, on your server, with no subscription and without entrusting your secrets to a third-party cloud? Vaultwarden is the ideal answer: it is a lightweight Rust reimplementation of the Bitwarden server, compatible with all official Bitwarden applications and extensions. It runs on less than 100 MB of RAM, making it deployable on the smallest VPS.

But a password manager is one of the most sensitive services you can host: it must enforce HTTPS, have a locked admin panel, and reliable backups. In this tutorial, we deploy Vaultwarden via Docker, behind a Caddy reverse proxy that handles HTTPS and automatic Let’s Encrypt certificates. Everything is in versioned, reproducible files, hardened for production.

Prerequisites

  • A secure VPS running Ubuntu 24.04 (or Debian 12). If you haven’t done so yet, follow our guide to install and secure an Ubuntu VPS first.

  • Docker and Docker Compose installed (installation command in step 1).

  • A domain name for which you control the DNS. We will use vault.exemple.fr as an example.

  • Ports 80 and 443 open on your UFW firewall: essential for Let’s Encrypt validation and HTTPS traffic.

  • A few minutes and some rigor: this is a critical service, so don’t rush it.

Step 1: Install Docker and Docker Compose

If Docker is not present, install it from the official repository (Ubuntu repository versions are often outdated):


sudo apt update

sudo apt install -y 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

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 -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Add your user to the docker group (log out and back in afterwards to apply changes):


sudo usermod -aG docker $USER

Verify:


docker --version && docker compose version

Step 2: Configure DNS

Caddy needs your domain to point to the VPS IP to issue the certificate. At your DNS provider, create an A record (and AAAA if IPv6):

| Type | Name | Value |

| :--- | :--- | :--- |

| A | vault.exemple.fr | 203.0.113.10 |

Wait for propagation, then verify from your machine:


dig +short vault.exemple.fr

The command should return your VPS IP. Until it does, Let’s Encrypt will fail to issue the certificate.

Step 3: Generate the Admin Token

Vaultwarden exposes an admin panel at /admin, protected by a token. This token must be an Argon2 hash, not a plain string. Generate it directly using the Vaultwarden image:


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

Enter a long, unique administrator password when prompted. It will display a string starting with $argon2id$.... Copy it carefully: this is what we will place in the configuration. Also note the corresponding plain-text password in your safe: this is the one you will type to access /admin.

Common Pitfall: If you paste the Argon2 hash into a .env file, the $ sign must be escaped ($$) because Docker Compose interprets it as a variable. We handle this in the next step.

Step 4: Create the Shared Docker Network

For Caddy to reach the Vaultwarden container by its name, both must share a dedicated Docker network. Create it once:


docker network create web

If you are already following our Caddy reverse proxy tutorial, this network might already exist: the command will return an error which is harmless, just ignore it.

Step 5: The Vaultwarden docker-compose.yml

Create a working directory and the composition file:


mkdir -p ~/vaultwarden && cd ~/vaultwarden

nano docker-compose.yml

services:

  vaultwarden:

    image: vaultwarden/server:latest

    container_name: vaultwarden

    restart: unless-stopped

    environment:

      # Disable public signups: no one can create an account

      SIGNUPS_ALLOWED: "false"

      # Allow manual user invitations from /admin

      INVITATIONS_ALLOWED: "true"

      # Public service URL (HTTPS required)

      DOMAIN: "https://vault.exemple.fr"

      # Argon2 hash of the admin token (note $$ instead of $)

      ADMIN_TOKEN: "${ADMIN_TOKEN}"

      # WebSockets for real-time client sync

      ENABLE_WEBSOCKET: "true"

    volumes:

      - ./vw-data:/data

    networks:

      - web



networks:

  web:

    external: true

Then create the .env file containing the token, by doubling every $ in the Argon2 hash:


nano .env

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

Replace with the hash generated in step 3, replacing each $ with $$. Single quotes protect the string.

Note the absence of a ports: section: Vaultwarden is never exposed directly to the Internet. It is only accessible through Caddy, via the internal web network. This is the fundamental security principle of this deployment.

Step 6: The Caddyfile for Automatic HTTPS

If you already have Caddy in place (see our dedicated tutorial), simply add the site block. Otherwise, create a Caddy directory:


mkdir -p ~/caddy && cd ~/caddy

nano Caddyfile

{

    # Email for Let's Encrypt notifications

    email admin@exemple.fr

}



vault.exemple.fr {

    # Let's Encrypt certificate and automatic HTTP -> HTTPS redirect

    encode gzip zstd



    # Security headers

    header {

        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

        X-Content-Type-Options "nosniff"

        X-Frame-Options "SAMEORIGIN"

        Referrer-Policy "strict-origin-when-cross-origin"

        -Server

    }



    # Route all traffic to the Vaultwarden container

    reverse_proxy vaultwarden:80

}

Then the Caddy docker-compose.yml in ~/caddy:


nano docker-compose.yml

services:

  caddy:

    image: caddy:2-alpine

    container_name: caddy

    restart: unless-stopped

    ports:

      - "80:80"

      - "443:443"

      - "443:443/udp"

    volumes:

      - ./Caddyfile:/etc/caddy/Caddyfile:ro

      - caddy_data:/data

      - caddy_config:/config

    networks:

      - web



volumes:

  caddy_data:

  caddy_config:



networks:

  web:

    external: true

The caddy_data volume is critical: it preserves your certificates. Without it, you would request a certificate on every restart and quickly hit Let’s Encrypt rate limits.

Step 7: Start the Services

Start Vaultwarden first, then Caddy:


cd ~/vaultwarden && docker compose up -d

cd ~/caddy && docker compose up -d

Follow Caddy logs to see the certificate issuance in real-time:


docker compose logs -f caddy

You should see a line like certificate obtained successfully for vault.exemple.fr. Then visit https://vault.exemple.fr: the Vaultwarden homepage will display with a valid padlock.

Step 8: Create Your Account and Invite Others

Since SIGNUPS_ALLOWED is set to false, no one can freely register. To create your first account, you have two options:

Option A (recommended): via the admin panel. Go to https://vault.exemple.fr/admin, enter the admin password (step 3), then use “Invite User” to invite yourself, as well as your family or team. Everyone receives a registration link.

Option B: temporarily open signups. Set SIGNUPS_ALLOWED to "true", restart, create accounts, then set it back to "false". Less clean, avoid this.

Once your account is created, install the Bitwarden browser extension or mobile app, and in the settings, configure the server URL to https://vault.exemple.fr before logging in. Everything else works exactly like Bitwarden.

Step 9: Enable Two-Factor Authentication

This is non-negotiable for a password vault. Once logged into the web interface:

  1. Go to Settings → Security → Two-step login.

  2. Enable TOTP (authenticator app) at a minimum, or ideally a WebAuthn/FIDO2 hardware key (YubiKey).

  3. Save your recovery code in a secure location outside the vault.

Without 2FA, your only defense is the master password. With it, even a compromised password is not enough for an attacker.

Step 10: Automatic Encrypted Backups

A vault without backups means all your access disappears at the first disk failure. Vaultwarden stores everything in ~/vaultwarden/vw-data (SQLite database, attachments, keys). Here is a daily backup script:


nano ~/vaultwarden/backup.sh

#!/bin/bash

set -euo pipefail

DATA_DIR="$HOME/vaultwarden/vw-data"

BACKUP_DIR="$HOME/vaultwarden/backups"

STAMP=$(date +%Y%m%d-%H%M%S)

mkdir -p "$BACKUP_DIR"



# Consistent backup of the SQLite database (native sqlite3 command in the container)

docker compose -f "$HOME/vaultwarden/docker-compose.yml" exec -T vaultwarden \

  /bin/sh -c "sqlite3 /data/db.sqlite3 '.backup /data/db-backup.sqlite3'"



# Archive all data

tar -czf "$BACKUP_DIR/vaultwarden-$STAMP.tar.gz" -C "$DATA_DIR" .



# Keep the last 14 days

find "$BACKUP_DIR" -name 'vaultwarden-*.tar.gz' -mtime +14 -delete

Make it executable and schedule it via cron (every day at 3 AM):


chmod +x ~/vaultwarden/backup.sh

(crontab -l 2>/dev/null; echo "0 3 * * * $HOME/vaultwarden/backup.sh") | crontab -

Important: A backup on the same server does not protect against fire, theft, or ransomware. Send these archives off-site and encrypted: our guide automatic backup with Restic and Backblaze shows how to replicate these backups to remote storage.

Final Verification

Check that everything is in place:


# Containers are running

docker ps --filter "name=vaultwarden" --filter "name=caddy"



# HTTP redirects to HTTPS (code 308)

curl -sI http://vault.exemple.fr | head -1



# HTTPS responds and the certificate is valid

curl -sI https://vault.exemple.fr | head -1



# Admin panel is NOT accessible without token (should ask for auth)

curl -sI https://vault.exemple.fr/admin | head -1

For a complete TLS audit, test your domain on SSL Labs: you should aim for A or A+ thanks to HSTS headers and Caddy’s modern protocols.

Security: Hardening Not to Be Neglected

Vaultwarden hosts your most sensitive secrets. Beyond HTTPS, apply these measures:

  • Very strong master password. This is the only key to your entire vault: a long, unique passphrase (minimum 4-5 random words). No encryption saves a vault protected by a weak password.

  • Locked /admin panel. The Argon2 token already protects it. To go further, restrict access to /admin by IP in Caddy, or disable it (DISABLE_ADMIN_TOKEN: "true") once configuration is complete.

  • Strict UFW firewall. Open only ports 80, 443, and SSH. Vaultwarden’s internal port 80 must never be exposed.

  • Fail2ban. Vaultwarden logs login attempts. Coupled with Fail2ban, you ban IPs attempting to brute-force an account. Enable logging (LOG_FILE) and create a dedicated rule.

  • Regular updates. Update the image (docker compose pull && docker compose up -d) after every backup, never before. A failed migration without a backup can corrupt the database.

  • Mandatory 2FA for all accounts (see step 9).

To go further on protecting an exposed VPS, consult our best practices in install and secure an Ubuntu VPS.

FAQ

Is Vaultwarden really compatible with Bitwarden apps?

Yes, completely. Vaultwarden implements the Bitwarden API, so the browser extension, iOS/Android mobile apps, desktop clients, and official CLI work without modification. You just need to configure the server URL to your Vaultwarden domain before logging in. The experience is identical to Bitwarden Cloud.

Why are my signups blocked?

This is intentional: SIGNUPS_ALLOWED: "false" prevents anyone from creating an account on your exposed instance. To add users, go through the /admin panel and use the invitation function. This is the recommended secure configuration for a public service.

The /admin panel returns an error or doesn’t accept my token, why?

In 90% of cases, it’s the Argon2 hash poorly escaped in the .env file: every $ in the hash must be doubled to $$ in Docker Compose. Also verify that you are entering the plain-text password (not the hash) in the /admin form. Regenerate the hash in step 3 if necessary.

Let’s Encrypt fails to issue the certificate, what should I do?

It’s almost always a DNS issue (the domain hasn’t pointed to the VPS yet) or a firewall issue (port 80 closed). Check dig +short vault.exemple.fr and sudo ufw status. Caddy logs (docker compose logs caddy) indicate the exact cause of the ACME rejection. During debugging, use Let’s Encrypt’s staging environment to avoid hitting rate limits.

Can I host Vaultwarden on my NAS instead of a VPS?

Yes, perfectly. Synology (Container Manager), QNAP (Container Station), and TrueNAS support Docker. The logic is identical: Vaultwarden container behind an HTTPS reverse proxy, never exposed directly. See our comparison Synology vs TrueNAS vs QNAP to choose the platform.

How do I migrate my passwords from another manager?

Export your data from your current manager (LastPass, 1Password, Bitwarden Cloud, KeePass…) in CSV or JSON format, then import them via the Bitwarden client connected to your Vaultwarden (Tools → Import Data). Then delete the unencrypted export file: it is a plain-text copy of all your secrets.

You now have a sovereign password manager, over HTTPS, backed up and hardened. It is one of the most useful services a homelab can offer. To follow Vaultwarden updates, security vulnerabilities, and self-hosting best practices, subscribe to our Telegram watch bot.

Tags: VaultwardenDockerCaddySelf-hostingHTTPSSecurityTutorialDevOps

Related

🛠️ Tutorials

Self-Hosting Your Website in 2026: Complete Guide (VPS, Docker, HTTPS)

2026 technical guide to self-hosting on a VPS: choosing plans, Docker setup, Let's Encrypt HTTPS, security, and real costs. Compare self-hosting vs. cloud.

Read
🛠️ Tutorials

Nextcloud All-in-One Docker Tutorial 2026: Full Setup & Auto HTTPS

2026 guide to deploying Nextcloud All-in-One with Docker. Covers master container, HTTPS reverse proxy, backups, hardening, and troubleshooting. Includes ready-to-use docker-compose and Caddy configs for your personal cloud.

Read
🛠️ Tutorials

Automated HTTPS Reverse Proxy with Caddy and Docker in 2026 (Effortless Let's Encrypt Certs)

2026 tutorial on deploying a Caddy reverse proxy with Docker: automatic HTTPS and Let's Encrypt certificates, multi-service routing, security headers, and seamless renewal. Ready-to-use Caddyfile and docker-compose configs.

Read