I built a Vaultwarden server in my home lab two years ago and never looked back. The whole thing runs on a small VPS with under 200 MB of RAM, syncs across my laptop, phone, and a spare tablet, and gives me full ownership of my password database. This guide walks you through setting up Vaultwarden as a self-hosted password manager with HTTPS from scratch, the same path I followed.
Vaultwarden is a lightweight, open-source, Rust rewrite of the Bitwarden server. It speaks the full Bitwarden REST API, which means the official Bitwarden browser extensions, mobile apps, and desktop clients connect to it without modification. Where the official Bitwarden self-host setup demands 11 Docker containers and roughly 2 GB of RAM, Vaultwarden runs as a single container and barely touches 100 MB at idle. That size advantage is the main reason the self-hosted community has flocked to it.
One requirement trips up every first-timer: HTTPS. The Vaultwarden Web Vault is a JavaScript application that uses the browser’s Web Crypto API. That API is only available in a “secure context,” which in practice means HTTPS (or localhost). So before you can load the web vault, you must serve Vaultwarden over HTTPS. This guide handles that end-to-end with Caddy, Nginx plus Certbot, or Cloudflare Tunnel.
Table of Contents
What You Need Before Setting Up Vaultwarden With HTTPS?
You can have a working Vaultwarden deployment up in about an hour if your prerequisites are already in place. I keep this list short because every extra dependency is something that can break later.
A Linux server with at least 1 vCPU, 1 GB of RAM, and 10 GB of disk. Debian 12, Ubuntu 24.04, Fedora 40, or any current LTS works. A Raspberry Pi 4 is fine for personal use.
A domain name you control. You’ll add an A (and AAAA) record pointing to your server’s public IP.
Ports 80 and 443 open in your firewall and forwarded to the server if it’s behind NAT. Caddy and Certbot both need port 80 for the HTTP-01 challenge.
Docker or Podman. Either runtime works. Pick Podman if you prefer rootless containers on Fedora, RHEL, or a hardened server.
DNS access to set records and, optionally, an API token for the DNS-01 challenge if you want wildcard certificates.
You don’t need a dedicated server. A small VPS from Hetzner, DigitalOcean, Vultr, or even Oracle’s free tier handles Vaultwarden with room to spare. If you’d rather avoid public HTTPS entirely, skip to the Tailscale section near the end of this guide.
Step 1: Install Docker or Podman
Docker is the most common choice for self-hosters, so I’ll start there. On Debian or Ubuntu, install Docker Engine and the Compose plugin with the official script.
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] 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-pluginAdd your user to the docker group so you don’t need sudo for every command, then verify the install.
sudo usermod -aG docker $USER
newgrp docker
docker --version
docker compose versionIf you’re on Fedora, RHEL, or another rpm-based distro and prefer rootless containers, use Podman instead. Podman ships in the default repos and Compose support is built in.
sudo dnf install -y podman podman-compose
podman --version
podman compose versionFor the rest of this guide I’ll show Docker commands. Podman users can substitute podman for docker and podman-compose for docker compose. Everything else stays the same.
Step 2: Deploy Vaultwarden With Docker Compose
Create a project directory and a docker-compose.yml file. I keep mine in /opt/vaultwarden so it’s easy to find later.
sudo mkdir -p /opt/vaultwarden/data
cd /opt/vaultwarden
sudo nano docker-compose.ymlPaste this configuration. Replace https://vault.example.com with your real domain, and set a long random string for ADMIN_TOKEN.
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: "https://vault.example.com"
SIGNUPS_ALLOWED: "true"
WEBSOCKET_ENABLED: "true"
ADMIN_TOKEN: "$(openssl rand -hex 32)"
ROCKET_PORT: "8080"
volumes:
- ./data:/data
ports:
- "127.0.0.1:8080:8080"
Two environment variables matter most at this stage. WEBSOCKET_ENABLED=true turns on the websocket endpoint on port 3012 that the mobile and desktop apps use for real-time sync. SIGNUPS_ALLOWED=true lets you create your first account; you’ll flip it to false once you’re in. Bind the port to 127.0.0.1 only, so the reverse proxy is the only path in.
Bring it up.
docker compose up -d
docker compose logs -f vaultwardenYou should see Rocket announce it’s listening on 8080. Hit Ctrl+C to exit the log tail. The container keeps running.
Step 3: Choose a Reverse Proxy for HTTPS
This is the decision that catches most first-time self-hosters. You have three solid options, and the right one depends on your priorities. I built the table below from running each of these in production for at least six months.
| Reverse Proxy | Auto HTTPS | Config Complexity | Ports Exposed | Best For |
|---|---|---|---|---|
| Caddy | Yes (Let’s Encrypt built-in) | Low | 80, 443 | Beginners, single-host setups |
| Nginx + Certbot | No (Certbot runs separately) | Medium-High | 80, 443 | Existing Nginx users, custom TLS settings |
| Cloudflare Tunnel | Yes (Cloudflare-managed cert) | Low | None (outbound only) | Users behind strict firewalls, no port forwarding |
If you’re new to reverse proxies, pick Caddy. It handles TLS certificate provisioning and renewal automatically, and the configuration is roughly ten lines for a complete Vaultwarden setup. If you already run Nginx for other services, stick with Nginx and add Certbot. If you’re behind CGNAT or simply don’t want to open any inbound ports, Cloudflare Tunnel is the cleanest option.
Step 4: Configure Caddy for Automatic HTTPS
Install Caddy on Debian or Ubuntu from the official repo.
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf "https://dl.cloudsmith.io/public/caddy/stable/gpg.key" | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf "https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt" | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install -y caddyReplace /etc/caddy/Caddyfile with your Vaultwarden site block.
vault.example.com {
encode gzip zstd
reverse_proxy 127.0.0.1:8080
reverse_proxy /notifications/hub 127.0.0.1:3012
}
The first reverse_proxy line sends normal traffic to the Rocket web server on 8080. The second forwards the websocket notifications endpoint on port 3012, which Vaultwarden uses when WEBSOCKET_ENABLED=true. Without that line, mobile and desktop clients won’t get real-time sync. Restart Caddy and check the certificate.
sudo systemctl restart caddy
sudo systemctl status caddy
curl -I https://vault.example.comIf curl -I returns a 200 OK with a valid certificate chain, you’re done. Caddy automatically renewed the cert from Let’s Encrypt the first time you requested it.
Step 5: Configure Nginx With Certbot for HTTPS
If Caddy isn’t your style, Nginx plus Certbot is the time-tested path. On Debian or Ubuntu:
sudo apt install -y nginx certbot python3-certbot-nginxDrop a server block at /etc/nginx/sites-available/vaultwarden.
server {
listen 80;
server_name vault.example.com;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name vault.example.com;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
ssl_certificate /etc/letsencrypt/live/vault.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vault.example.com/privkey.pem;
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Content-Type-Options "nosniff" always;
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
location /notifications/hub {
proxy_pass http://127.0.0.1:3012;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /notifications/hub/negotiate {
proxy_pass http://127.0.0.1:8080;
}
}
Enable the site, run Certbot to fetch a certificate, and reload.
sudo ln -s /etc/nginx/sites-available/vaultwarden /etc/nginx/sites-enabled/
sudo certbot --nginx -d vault.example.com
sudo systemctl reload nginxFor a wildcard certificate, switch to the DNS-01 challenge. Certbot supports most major DNS providers via plugins.
sudo certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/.secrets/cloudflare.ini -d "vault.example.com" -d "*.vault.example.com"DNS-01 is the only path that works if your server isn’t reachable from the public internet, which is useful for split-horizon DNS or Tailscale-only deployments.
Step 6: Finish the Vaultwarden Initial Setup
Browse to https://vault.example.com and create your first account. This is the only moment SIGNUPS_ALLOWED=true should be true. Once your account exists, edit docker-compose.yml and flip it.
environment:
SIGNUPS_ALLOWED: "false"
INVITATIONS_ALLOWED: "true"
WEBSOCKET_ENABLED: "true"Then restart the container so the new value takes effect.
docker compose up -dNow enable two-factor authentication on your account from the Web Vault settings. TOTP authenticator apps like Aegis, Ente Auth, or 2FAS work well. I also enable a YubiKey for U2F as a second factor. With both, a stolen master password alone can’t unlock your vault.
To access the admin panel, append /admin to your Vaultwarden URL and paste the ADMIN_TOKEN you set in docker-compose.yml. From the admin panel you can send email invitations, check the version, and rotate the SMTP credentials. Store the token in a password manager (preferably this one once you’ve finished setup) rather than in plaintext files.
Step 7: Connect the Bitwarden Apps to Your Self-Hosted Vaultwarden
The official Bitwarden clients work against Vaultwarden because Vaultwarden implements the same REST API. You just point them at your server instead of vault.bitwarden.com.
In the browser extension, open Settings and scroll to “Self-hosted.” Enter https://vault.example.com as the Server URL and save. The extension will re-prompt for your master password and then sync your vault. On iOS and Android, open the app, tap the gear icon, choose “Self-hosted (custom server),” and paste the same URL.
On desktop, the Bitwarden desktop app uses the same self-hosted field under Settings. After login, you should see a green checkmark next to your server URL and a successful sync indicator. If the app complains about a certificate error, double-check that the certificate chain on your reverse proxy is complete (no missing intermediate certificates).
Step 8: Back Up the Vaultwarden SQLite Database
A self-hosted password manager without backups is a single point of failure waiting to fail. Vaultwarden stores everything in a single SQLite file inside /data/db.sqlite3. Don’t just copy it while Vaultwarden is running; SQLite may flush a partial page and corrupt the file. Use the built-in .backup command instead.
sudo apt install -y sqlite3
sudo sqlite3 /opt/vaultwarden/data/db.sqlite3 ".backup '/opt/vaultwarden/backups/db-$(date +%F).sqlite3'"Then push the snapshot somewhere off-server. I use restic against Backblaze B2 because the cost is roughly $0.005 per GB per month.
sudo apt install -y restic
restic -r b2:vaultwarden-backups:/ init
restic -r b2:vaultwarden-backups:/ backup /opt/vaultwarden/backups
restic -r b2:vaultwarden-backups:/ forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6Automate it with a systemd timer so backups run nightly without me thinking about it.
/etc/systemd/system/vaultwarden-backup.service
[Unit]
Description=Vaultwarden nightly backup
Wants=vaultwarden-backup.timer
[Service]
Type=oneshot
ExecStart=/usr/local/bin/vaultwarden-backup.sh
/etc/systemd/system/vaultwarden-backup.timer
[Unit]
Description=Run Vaultwarden backup nightly
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now vaultwarden-backup.timerI verify a backup by restoring it to a throwaway container once a month. A backup you haven’t tested is a backup you don’t have.
Setting Up Vaultwarden as a Self-Hosted Password Manager With HTTPS on Windows Using WSL2
A lot of Eglug readers run Windows as their daily driver. You can still set up Vaultwarden with HTTPS on Windows by using WSL2 (Windows Subsystem for Linux) with the Docker Desktop backend.
Install WSL2 with Ubuntu 24.04 from the Microsoft Store.
Install Docker Desktop and enable “Use the WSL 2 based engine” in Settings.
In PowerShell, run
wsl --set-default-version 2andwsl --set-data-root D:wslif you want your distro’s storage on a different drive.Open the Ubuntu app and follow Steps 1-8 of this guide as if you were on a Linux VPS.
If you need to reach Vaultwarden from another Windows app, expose the WSL port back to the host with
netsh interface portproxy add v4tov4 listenport=443 listenaddress=0.0.0.0 connectport=443 connectaddress=$(wsl hostname -I).
Windows Firewall may block the inbound 443 port from other devices. Allow it with New-NetFirewallRule -DisplayName "Vaultwarden" -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow if needed.
Using Tailscale as a No-Port HTTPS Alternative
If you only use Vaultwarden from devices you control, you don’t actually need public HTTPS. Tailscale creates a WireGuard mesh VPN between your devices and your server, and your server never opens a public port. This sidesteps both the HTTPS requirement (because Tailscale gives every device a stable hostname) and the need to expose your home network.
Install Tailscale on your server with curl -fsSL https://tailscale.com/install.sh | sh and sudo tailscale up. Install it on each device you want to sync from and sign in with the same account. Then use the Tailscale IP or magicDNS hostname (something like vaultwarden.tail-xxxx.ts.net) as the server URL in the Bitwarden clients.
You still want HTTPS inside the Tailscale network, but a self-signed certificate works because you control the trust. Caddy can issue a Tailscale cert with the tailscale-cert plugin, or you can run Vaultwarden behind Caddy with internal HTTPS only.
Vaultwarden HTTPS Setup: Troubleshooting Common Errors
Most of the questions on r/selfhosted and r/vaultwarden about HTTPS come down to the same handful of problems. Here’s the checklist I run when someone reports an issue.
502 Bad Gateway. The reverse proxy can’t reach Vaultwarden. Confirm the container is up with
docker compose psand that the port mapping is correct. From the server,curl http://127.0.0.1:8080should return the Vaultwarden homepage.Certbot HTTP-01 challenge failed. Port 80 isn’t reachable from Let’s Encrypt. Check the firewall with
sudo ufw statusor your cloud provider’s security group. Caddy needs the same access.Web Crypto API errors on http://. The browser refuses to load the web vault because the page isn’t in a secure context. You must use HTTPS, even with a self-signed cert for testing.
Mobile app sync stuck on “Last sync: never”. The websocket port 3012 isn’t reachable from the client. Confirm
WEBSOCKET_ENABLED=truein the container environment and that your reverse proxy forwards/notifications/hub.Certificate renewed but browser still warns. Your reverse proxy is serving the old fullchain. Restart the reverse proxy after Certbot renews, or use the deploy hook
--deploy-hook "systemctl reload nginx".Vaultwarden responds on localhost but not the domain. The
DOMAINenv var is wrong or missing. Updatedocker-compose.ymland restart the container.
For deeper debugging, run docker compose logs --tail=200 vaultwarden and check the reverse proxy logs at /var/log/caddy or /var/log/nginx/error.log. Most errors are obvious once you see them.
Security Hardening Checklist After Going Live
Once everything works, spend an hour on hardening. The list below covers the changes I make on every Vaultwarden deployment I touch.
Disable public signups. Set
SIGNUPS_ALLOWED=falseafter creating your account. Send invitations through the admin panel only when needed.Enable 2FA and U2F. TOTP plus a hardware key like a YubiKey or OnlyKey makes credential theft dramatically harder.
Restrict the firewall. Allow ports 22, 80, and 443 with
ufw. Drop everything else. On cloud VPSs, lock down the provider’s security group to your home IP for SSH.Install fail2ban or CrowdSec. CrowdSec is the modern choice. It shares blocklists across the community and is easier to tune than fail2ban.
Enable automatic updates. Watchtower rebuilds your Vaultwarden container when a new image ships, or use a systemd timer with
docker compose pull && docker compose up -dif you want predictable update windows.Forward email through a real SMTP relay. Don’t run a raw SMTP server. Use Mailgun, Amazon SES, or your existing transactional email provider.
Audit logs once a month. The admin panel shows failed logins, new device connections, and password changes. Review them for unexpected activity.
Vaultwarden HTTPS Setup FAQ
Does Vaultwarden need HTTPS?
Yes. The Vaultwarden Web Vault uses the browser Web Crypto API, which only works in a secure context. That means HTTPS or localhost. Without HTTPS, the web vault will refuse to load.
How do I enable HTTPS on Vaultwarden?
Put a reverse proxy like Caddy or Nginx in front of Vaultwarden and let it terminate TLS. Caddy automatically fetches a Let’s Encrypt certificate. With Nginx, run Certbot to obtain and renew the cert.
How do I set up Vaultwarden with Docker?
Write a docker-compose.yml that pulls the vaultwarden/server image, mounts a data volume, and exposes port 8080 to localhost. Set DOMAIN, WEBSOCKET_ENABLED=true, and ADMIN_TOKEN env vars, then run docker compose up -d.
Can I use the Bitwarden apps with Vaultwarden?
Yes. Vaultwarden implements the full Bitwarden REST API, so the official browser extensions, mobile apps, and desktop clients connect to it by entering your self-hosted URL in Settings.
Is a certificate necessary for Vaultwarden to load in self-hosted mode?
Yes for the Web Vault, because the browser requires a secure context for Web Crypto. Bitwarden CLI and the desktop app can connect over HTTP on localhost, but the web vault needs HTTPS.
How to set up Vaultwarden with HTTPS on Windows?
Install WSL2 with Ubuntu, enable the Docker Desktop WSL2 backend, and follow the Linux steps. Use netsh interface portproxy to forward Windows host ports to the WSL2 IP if needed.
Final Thoughts on Running Your Own Vaultwarden
Setting up Vaultwarden as a self-hosted password manager with HTTPS takes an afternoon and gives you full ownership of your credentials. The pieces all fit together: a single Docker container for the server, Caddy or Nginx for the TLS handshake, Let’s Encrypt for the certificate, and the same Bitwarden clients you already trust. The hardest part is the reverse proxy choice, and the table in Step 3 should make that call easy.
Once it’s running, commit to three habits. Back up the SQLite database nightly, with at least one off-site copy. Enable two-factor authentication plus a hardware key on your own account. Review the admin audit logs once a month. Those three habits are the difference between a self-hosted Vaultwarden that runs for years and one that disappears the day your SSD fails.
That’s the full loop. I still get a small thrill every time I unlock my vault knowing no third party holds my data. Welcome to the self-hosted club.