The marketing for self-hosted Bitwarden positions it as a complex enterprise undertaking. The official Bitwarden self-hosted server requires multiple Docker containers, recommends 2 GB of RAM minimum, and has a documented installation process that runs to 14 pages of instructions. For a single user wanting their own password vault, that complexity is overkill.
The community-built alternative, Vaultwarden, is API-compatible with the official Bitwarden clients (mobile apps, browser extensions, desktop apps, CLI), runs in a single Docker container, fits comfortably in 256 MB of RAM, and installs in roughly fifteen minutes if you have a basic VPS already running. This guide walks through the entire setup on a $5 per month Hetzner CX11 instance. The same instructions apply with minor adjustments to DigitalOcean, Linode, or any other Linux VPS.
By the end of this you will have a private password manager that you control, accessible from any device through the standard Bitwarden apps, with HTTPS, automatic backups, and reasonable security defaults.
What you need before starting
Three things, listed in order of acquisition.
A domain name. Any domain works. The cost is around $10 to $15 per year. We will create a subdomain like vault.yourdomain.com for the vault. If you do not own a domain yet, register one at Cloudflare Registrar (cheapest, requires using Cloudflare DNS) or Porkbun (cheap, flexible).
A VPS. The walkthrough uses Hetzner CX11 (€3.79 per month, 2 GB RAM, 1 vCPU) but any VPS with at least 1 GB of RAM and a Linux distribution works. DigitalOcean’s $6 droplet, Linode’s Nanode 1 GB, or Vultr’s $6 instance all fit.
An email address you can receive at. Vaultwarden uses email for password reset, account verification on first signup, and similar flows. Cloudflare Email Routing (free, forwards to your real address) is fine.
You also need basic comfort with SSH, the command line, and DNS. If you have followed any “set up a Linux server” tutorial in the past few years, you are ready.
Total time investment: 30 to 60 minutes for someone who has done similar work, 90 to 120 minutes for someone learning as they go.
Step 1 — Provision the VPS
Sign up at Hetzner Cloud (Cloud-hosting provider for developers & teams), create a new project, then create a server. Pick:
- Location: closest to where you live (Falkenstein for Europe, Ashburn for US East coast)
- Image: Ubuntu 24.04 LTS
- Type: CX11 (or CX22 if you want headroom for future apps)
- Networking: enable IPv4 and IPv6
- SSH key: paste in your public key (do not use password auth)
- Firewall: leave default for now, we will configure it after
After about 30 seconds the server is ready. Note the IPv4 address.
SSH in: ssh [email protected]
Update the system:
apt update && apt upgrade -y
apt install -y ufw fail2ban
Configure the firewall:
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
Configure fail2ban (it works out of the box for SSH, but enable the service):
systemctl enable --now fail2ban
This is your baseline secure server.
Step 2 — Point your domain at the server
In your DNS provider’s control panel, add an A record for the subdomain you want to use. For example, if your domain is example.com and you want vault.example.com for the vault:
- Type: A
- Name: vault
- Value: your.server.ip
- TTL: 300 (or auto)
- Proxy status (Cloudflare specific): OFF for now (we need direct access for SSL cert validation; can be re-enabled later)
DNS changes propagate in a few minutes. Verify with:
dig vault.example.com +short
You should see your server IP.
Step 3 — Install Docker
Vaultwarden runs as a Docker container. Install Docker via the official method:
apt install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
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" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Verify: docker --version should show the installed version.
Step 4 — Set up Caddy as the reverse proxy
We use Caddy because it handles HTTPS automatically with Let’s Encrypt, has clean configuration, and is dramatically simpler than nginx for this use case.
Create a working directory:
mkdir -p /opt/vaultwarden
cd /opt/vaultwarden
Create docker-compose.yml:
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: "https://vault.example.com"
SIGNUPS_ALLOWED: "true"
SIGNUPS_DOMAINS_WHITELIST: "yourdomain.com"
ADMIN_TOKEN: "REPLACE_WITH_RANDOM_64_CHAR_STRING"
WEB_VAULT_ENABLED: "true"
volumes:
- ./vw-data:/data
ports:
- "127.0.0.1:8080:80"
caddy:
image: caddy:2
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./caddy-data:/data
- ./caddy-config:/config
Replace vault.example.com and yourdomain.com with your actual domain. Generate a random ADMIN_TOKEN:
openssl rand -base64 48
Paste that output as ADMIN_TOKEN.
Create Caddyfile:
vault.example.com {
reverse_proxy vaultwarden:80
}
Replace the domain.
Start everything:
docker compose up -d
Watch the logs to confirm Caddy gets a certificate from Let’s Encrypt:
docker compose logs -f caddy
You should see something like “certificate obtained successfully” within a minute. If you see errors, the most common causes are: DNS has not propagated yet, or your firewall is blocking port 80.
Visit https://vault.example.com in a browser. You should see the Bitwarden login page.
Step 5 — Create your account and import existing passwords
Click “Create account” and register with the email address you set up earlier. The first user to sign up gets registered automatically. After your account exists, you should disable signups by editing docker-compose.yml:
SIGNUPS_ALLOWED: "false"
Then restart Vaultwarden:
docker compose up -d
This prevents anyone discovering your URL from creating an account.
If you are migrating from another password manager, export from there as a CSV, then in Bitwarden web vault: Tools, Import data, select the source format, paste/upload the export. Verify a few entries imported correctly. Delete the local CSV (it is plaintext passwords).
Install the Bitwarden client on your devices:
- Browser: Bitwarden extension for Firefox, Chrome, Safari, Edge
- Desktop: Bitwarden desktop app
- Mobile: Bitwarden iOS, Bitwarden Android
When logging in on each client, use “Self-hosted” option in the settings, set the server URL to your https://vault.example.com, then login with your account.
Step 6 — Backups
Without backups, you have a private password vault that is one server crash away from a disaster. Set up automated backups now.
Vaultwarden’s data lives in ./vw-data. The simplest backup approach: rsync to a remote location daily.
Add to root’s crontab (crontab -e):
0 3 * * * tar -czf /tmp/vw-backup-$(date +\%Y\%m\%d).tar.gz /opt/vaultwarden/vw-data && rclone copy /tmp/vw-backup-*.tar.gz remote:vault-backups/ && rm /tmp/vw-backup-*.tar.gz
You need rclone configured pointing at a remote (Backblaze B2, Cloudflare R2, your home NAS via Tailscale, anywhere). Set it up beforehand.
Critically: practice a restore. Spin up a fresh test VPS, install Vaultwarden, restore from your backup, verify your vault opens. The first time you actually need to restore will be the worst time to discover the backup process is broken.
Step 7 — Maintenance
Three things, on a schedule.
Weekly, check docker compose logs vaultwarden for unusual errors.
Monthly, update the containers:
cd /opt/vaultwarden
docker compose pull
docker compose up -d
Quarterly, verify backups are running and test a restore.
Vaultwarden does not have a desktop client of its own, but it works with the official Bitwarden apps so update those normally through your OS package manager or app store.
What this setup costs
| Item | Annual cost |
|---|---|
| Hetzner CX11 VPS | €45 (about $50) |
| Domain | $10 to $15 |
| Backup storage (Cloudflare R2 free tier covers small vaults) | $0 to $5 |
| Total | $60 to $70 per year |
Compare to Bitwarden Premium ($10 per year) or 1Password Individual ($35 per year). The self-hosted version is more expensive in pure dollar terms, but you control the hosting, the data, and the upgrade timing.
The math changes if you put other services on the same VPS. Adding Pi-hole, Tailscale, and a personal blog to the same Hetzner instance brings the per-service cost down significantly.
Common problems
The Caddy logs say “no such record” or similar SSL errors. DNS has not propagated. Wait fifteen minutes and try again, or check dig resolution.
Vaultwarden returns “internal server error.” Check docker compose logs vaultwarden for the actual error. Most commonly, the data volume permissions are wrong; rerun docker compose down && docker compose up -d.
The mobile app cannot connect. The “Self-hosted” URL must be exact, including https://. Some users miss the protocol prefix.
You forgot the admin token. Look in the docker-compose.yml. The token is what you set; you can change it and restart the container.
You want to add 2FA. Bitwarden supports TOTP, FIDO2, and email-based 2FA. Configure them in your account settings. WebAuthn with a YubiKey is the strongest option.
When self-hosting Vaultwarden is not the right call
You are completely new to running servers. Use Bitwarden’s free hosted tier. Save self-hosting for after you have run a few less-critical services first.
You have an unreliable internet connection at home and the VPS is at home. Vaults sync via the server, so VPS downtime means devices cannot sync new entries. Put the VPS at a real provider, not at home, unless your home connection is enterprise-grade.
You are managing this for a non-technical family member. The mobile app self-hosted setup confuses non-technical users. Pay for Bitwarden Premium ($10 per year) and let them use the official servers.
Why this approach works
Vaultwarden is open source, written in Rust (memory-safe), and audited by independent reviewers. The Bitwarden clients are also open source and audited. The data on the server is encrypted such that even with full server access, an attacker cannot read your vault without your master password.
The architecture is simple by design. You can read the entire docker-compose configuration in two minutes. There are no hidden services, no required telemetry, no opaque background processes. If something breaks, you can debug it.
This setup has been running for me, in roughly this configuration, for over three years. Total downtime is a few hours, all attributable to my own VPS provider’s regional incidents. Total maintenance time after initial setup is roughly 30 minutes per quarter.
If you have been thinking about owning your password infrastructure, this is the realistic version of that decision. It is not free, it is not effortless, but it is achievable in an evening and the result genuinely belongs to you.
Vaultwarden GitHub | Hetzner Cloud | Caddy
Related: VPS specs guide for self-hosted apps, 13 things I wish someone had told me before self-hosting