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.
How to deploy Vaultwarden, front it with Caddy for HTTPS, wire up SMTP, and preserve the whole data directory for recovery.
Operators who understand that a password manager is a trust-heavy service and are willing to keep the deployment path disciplined.
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.comalready pointed at your server. - Docker Compose installed and no other service owning ports
80and443. - 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:
- Open
https://vault.example.comimmediately after startup and create the first account while bootstrap signups are still enabled. - Sign in to the admin panel only from a trusted path and verify your configuration.
- Trigger a test email workflow such as an invite or password reset.
- Disable open signup right after the first account exists, then redeploy the stack.
- Confirm the
vw-datadirectory 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
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.