Install Vaultwarden With Docker Compose and HTTPS

Run Vaultwarden as a lean self-hosted password manager, terminate TLS in front of it, and treat backups plus email delivery as first-class parts of the setup instead of afterthoughts.

Vaultwarden Docker Compose HTTPS + SMTP
Illustrated guide cover for Install Vaultwarden With Docker Compose and HTTPS
Vaultwarden • Docker Compose • HTTPS + SMTP
What you learn

How to deploy Vaultwarden, front it with Caddy for HTTPS, wire up SMTP, and preserve the whole data directory for recovery.

Best for

Operators who understand that a password manager is a trust-heavy service and are willing to keep the deployment path disciplined.

Risk to watch

A password manager that "seems up" but lacks working email or tested backups is not done. It is waiting to fail at the worst moment.

Before you begin

  • A domain name such as vault.example.com already pointed at your server.
  • Docker Compose installed and no other service owning ports 80 and 443.
  • An SMTP relay you trust for password-reset and invite mail.
  • A backup location outside the server itself.

Vaultwarden's README documents a minimal Compose pattern that mounts ./vw-data to /data and sets the DOMAIN variable. The project's environment template also calls out that mail only works when you set SMTP_FROM and either SMTP_HOST or a sendmail path, and that DOMAIN should match the public URL so email links stay correct.

Step 1: Create the project layout

mkdir -p ~/apps/vaultwarden
cd ~/apps/vaultwarden
mkdir -p vw-data

Create a local environment file and keep secrets out of the Compose YAML itself:

cat > .env <<'EOF'
VW_DOMAIN=https://vault.example.com
VW_ADMIN_TOKEN=replace-this-with-a-long-random-secret
SMTP_HOST=smtp.example.com
SMTP_FROM=vaultwarden@example.com
SMTP_PORT=587
SMTP_SECURITY=starttls
SMTP_USERNAME=vaultwarden@example.com
SMTP_PASSWORD=replace-this-with-the-real-smtp-password
EOF

Step 2: Define the Compose stack

Save this as docker-compose.yml:

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    env_file:
      - .env
    environment:
      DOMAIN: ${VW_DOMAIN}
      SIGNUPS_ALLOWED: "true"
      ADMIN_TOKEN: ${VW_ADMIN_TOKEN}
      SMTP_HOST: ${SMTP_HOST}
      SMTP_FROM: ${SMTP_FROM}
      SMTP_PORT: ${SMTP_PORT}
      SMTP_SECURITY: ${SMTP_SECURITY}
      SMTP_USERNAME: ${SMTP_USERNAME}
      SMTP_PASSWORD: ${SMTP_PASSWORD}
    volumes:
      - ./vw-data:/data
    networks:
      - vaultnet

  caddy:
    image: caddy:2
    container_name: vaultwarden-caddy
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - vaultnet

networks:
  vaultnet:

volumes:
  caddy_data:
  caddy_config:

Keeping Vaultwarden on an internal Docker network and letting Caddy own the public ports is simpler than publishing the app container directly and trying to bolt TLS on later.

Step 3: Add HTTPS with Caddy

Save this as Caddyfile:

vault.example.com {
  reverse_proxy vaultwarden:80
}

Start the stack:

docker compose up -d
docker compose ps

Caddy should obtain certificates automatically once DNS and ports are correct. If you already use another reverse proxy platform, keep the same private-by-default idea and adapt the proxy layer there instead of running two public proxies on one host.

Step 4: Verify sign-in, SMTP, and recovery basics

Do not stop at "the homepage loads." Verify the parts that matter:

  1. Open https://vault.example.com immediately after startup and create the first account while bootstrap signups are still enabled.
  2. Sign in to the admin panel only from a trusted path and verify your configuration.
  3. Trigger a test email workflow such as an invite or password reset.
  4. Disable open signup right after the first account exists, then redeploy the stack.
  5. Confirm the vw-data directory contains the database and uploaded data you plan to protect.
docker compose logs vaultwarden --tail 100
docker compose logs caddy --tail 50
sed -i 's/SIGNUPS_ALLOWED: "true"/SIGNUPS_ALLOWED: "false"/' docker-compose.yml
docker compose up -d
ls -lah vw-data

If you do not want even a brief open-signup window, use the admin panel to invite the first account instead and complete registration with that exact email address. Either way, do not leave public signups enabled once the bootstrap step is complete.

If SMTP is broken, fix that before you call the deployment complete. Account recovery mail is not a decorative feature for this kind of service.

Step 5: Operate it with real caution

After bootstrap, keep signups disabled unless you intentionally run a multi-user instance, and store the admin token like any other high-trust credential. Avoid the temptation to keep a password manager "temporary" and badly protected while you sort out the rest later.

Back up the entire data directory on a regular cadence:

docker compose stop vaultwarden
tar -czf vaultwarden-backup-$(date +%F).tar.gz vw-data
docker compose start vaultwarden
Warning: only counting on snapshots from the same server is not a backup strategy. If the box dies or you corrupt the volume, you need an off-host recovery path.

Rollback and update notes

Take a backup before upgrading images, then recreate the service cleanly. If an update causes problems, restore the known-good data directory before experimenting further.

docker compose pull
docker compose up -d

Document where the backup archive lives and who can decrypt or access it. Recovery without access discipline just creates a second trust problem.

Troubleshooting

The site loads but reset emails never arrive.
Recheck the SMTP values, watch the Vaultwarden logs during a test, and make sure the host can actually reach the SMTP service.

The email links point to the wrong host.
Your DOMAIN value is wrong or stale. Fix that before sending more mail.

You are unsure whether backups are enough.
Restore the archive on a disposable test path and inspect its contents. Confidence without a restore check is fake confidence.

You want a public service but are nervous about exposure.
Good. Keep the proxy and password policies tight, patch regularly, and avoid stacking unrelated risky experiments on the same host.