Tutorials 11 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 · no sponsored content
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 funds the site). As an Amazon Associate, Selfhostr earns from qualifying purchases. 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.

🖥️ Hardware to host it at home — To run this service 24/7 without a big power bill, a low-power mini PC is enough in most cases. Two common picks: the Beelink Mini S12 Pro (Intel N100, 16 GB) and the Beelink EQ14 (Intel N150, dual 2.5 GbE ports). To choose for your needs: our guide to the best mini PCs for a homelab.

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.

Turnkey alternative: NordPass

Self-hosting your password manager means owning backups, updates and uptime. If you would rather use an end-to-end encrypted service with nothing to administer, NordPass is a credible alternative. See NordPass

Step 1: Install Docker and Docker Compose

If Docker is not present, install it from the official repository (Ubuntu repository versions are often outdated). On Debian 12, replace ubuntu with debian in both download.docker.com URLs:

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):

TypeNameValue
Avault.exemple.fr203.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 directly into the environment: section of docker-compose.yml, every $ must be escaped ($$) because Docker Compose interprets it as a variable. In a .env file, between single quotes, no escaping is needed. 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 organization owners/admins to invite users
      INVITATIONS_ALLOWED: "true"
      # Public service URL (HTTPS required)
      DOMAIN: "https://vault.exemple.fr"
      # Argon2 hash of the admin token, read from the .env file
      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, pasting the Argon2 hash as is, between single quotes (do not double the $):

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

Replace with the full hash generated in step 3. Single quotes prevent Docker Compose from interpreting the $: do not double them here.

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 by email if an SMTP server is configured; without SMTP, no email is sent and the invited person simply creates their account with that address from the homepage.

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 (Vaultwarden's built-in backup command, creates a db_*.sqlite3 file in /data)
docker compose -f "$HOME/vaultwarden/docker-compose.yml" exec -T vaultwarden \
  /vaultwarden backup

# 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 answers with its login page (code 200): the admin password is still required to enter
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 by removing the ADMIN_TOKEN variable (never set DISABLE_ADMIN_TOKEN, which instead removes all authentication) 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 a badly escaped Argon2 hash: in the .env file, between single quotes, it is pasted as is; only when written directly in environment: of docker-compose.yml must every $ be doubled to $$. 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 budget. 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