Setting up a self-hosted Vaultwarden password manager is one of the simplest and most rewarding things you can do in your homelab. A single Docker container, a few environment variables, and you have a fully functional password vault — compatible with every official Bitwarden app and browser extension. No multi-container orchestration, no broken internal services, no hassle. 🛡️
In this guide I’ll walk you through the complete process:
- Install — deploy a self-hosted Vaultwarden password manager on Docker with OpenMediaVault 🐳
- Secure — put it behind Nginx Proxy Manager with HTTPS and lock down signups + admin panel 🔒
- Maintain — update the container and back up your vault reliably ♻️
Why Vaultwarden? 🤔
Vaultwarden is a community-built, Rust-based server that implements the Bitwarden API. It’s fully compatible with all official Bitwarden clients — browser extensions, mobile apps, desktop apps, and the CLI.
Why many self-hosters prefer it:
- Extremely lightweight — runs on ~10 MB of RAM at idle. One container, built-in SQLite, built-in web vault 🪶
- All premium features unlocked — TOTP authenticator, file attachments, emergency access, organizations — without a paid plan ⚡
- Just works — no Installation ID/Key required, no phone-home to Bitwarden’s cloud, no internal service discovery bugs 🎯
- Active development — large community, frequent releases, solid wiki documentation 📖
💡 Vaultwarden is not an official Bitwarden product. It’s an independent reimplementation. But in practice, for personal and homelab use, it’s the go-to choice for a self-hosted Vaultwarden password manager setup.
Prerequisites ✅
Here’s what you need to set up a self-hosted Vaultwarden password manager on Docker:
- A Linux server with Docker Engine and Docker Compose installed. New to Docker? Start with Docker for Beginners: Containers, Images, Volumes, Compose
- Nginx Proxy Manager (NPM) running and accessible (same or separate machine)
- A domain name (e.g.
vault.example.com) with DNS pointing to your public IP - Router port forwarding: TCP 80 and 443 → your NPM host
Quick check:
docker --version
docker compose version
🎯 Reference IPs used in this guide (replace with yours):
- Docker host (OMV):
10.0.0.20- NPM host:
10.0.0.10- Domain:
vault.example.com
Step 1 — Create the Project Directory 📁
SSH into your Docker host:
sudo mkdir -p /srv/docker/vaultwarden
cd /srv/docker/vaultwarden
Step 2 — Create docker-compose.yml 🐳
sudo nano /srv/docker/vaultwarden/docker-compose.yml
Paste:
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: "https://vault.example.com"
SIGNUPS_ALLOWED: "true"
volumes:
- ./vw-data:/data
ports:
- "10.0.0.20:8085:80"
⚠️ Replace 10.0.0.20 with your actual server IP and vault.example.com with your domain.
What each setting does:
DOMAIN— tells Vaultwarden the URL it’s served at (needed for HTTPS links in emails and the web vault)SIGNUPS_ALLOWED: "true"— lets you create your first account. We’ll disable this after signup 🔒./vw-data:/data— all persistent data (SQLite database, RSA keys, attachments) lives in this folder10.0.0.20:8085:80— binds port 8085 on your LAN IP only (not exposed on all interfaces)
Step 3 — Launch Vaultwarden ▶️
cd /srv/docker/vaultwarden
sudo docker compose up -d
Check that it’s running:
sudo docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
Test from any LAN machine:
curl -I http://10.0.0.20:8085
You should see HTTP/1.1 200 OK. That’s it — Vaultwarden is alive. No cryptic logs, no crashing nginx, no multi-service orchestration. 🎉
If something goes wrong:
sudo docker logs --tail 50 vaultwarden
For real-time container log monitoring, Dozzle is a great lightweight option. 📊
Step 4 — Set Up HTTPS via Nginx Proxy Manager 🌍🔒
Open your NPM dashboard (10.0.0.10) → Hosts → Proxy Hosts → Add Proxy Host.
Details tab:
- Domain Names:
vault.example.com - Scheme:
http - Forward Hostname / IP:
10.0.0.20 - Forward Port:
8085 - ✅ Websockets Support
- ✅ Block Common Exploits
SSL tab:
- Request a new Let’s Encrypt certificate
- ✅ Force SSL
Save, wait a few seconds, then open:
https://vault.example.com
You should see the Vaultwarden web vault. Create your account and set a strong master password. Your self-hosted Vaultwarden password manager is now live! 🎉
For extra protection against brute-force attacks, add CrowdSec with Nginx Proxy Manager for real-time intrusion detection. 🛡️
Step 5 — Lock It Down After First Signup 🔒
Once your account is created, disable public registration immediately:
sudo nano /srv/docker/vaultwarden/docker-compose.yml
Change:
SIGNUPS_ALLOWED: "false"
Restart:
cd /srv/docker/vaultwarden
sudo docker compose down
sudo docker compose up -d
Now nobody else can register. If you ever need to invite someone, use the admin panel (next section) or temporarily re-enable signups.
Optional: Enable the Admin Panel 🛠️
The admin panel lets you manage users, view registrations, and change server settings via a web UI at https://vault.example.com/admin.
Generate a secure admin token using Vaultwarden’s built-in hasher:
sudo docker exec -it vaultwarden /vaultwarden hash
It will ask you to enter and confirm a password. The output is an Argon2 hash. Copy it — it looks like:
$argon2id$v=19$m=65540,t=3,p=4$...long-hash...
Add it to your docker-compose.yml under environment:
environment:
DOMAIN: "https://vault.example.com"
SIGNUPS_ALLOWED: "false"
ADMIN_TOKEN: "$argon2id$v=19$m=65540,t=3,p=4$YOUR_HASH_HERE"
⚠️ Important: wrap the hash value in double quotes in the YAML file. If you use a .env file instead, use single quotes and replace every $ with $$.
Restart:
cd /srv/docker/vaultwarden
sudo docker compose down
sudo docker compose up -d
Now go to https://vault.example.com/admin and log in with the password you typed (not the hash). 🔐
Updating Your Self-Hosted Vaultwarden Password Manager ♻️
Keeping your self-hosted Vaultwarden password manager updated is straightforward — and it’s good practice to back up first (see next section):
cd /srv/docker/vaultwarden
sudo docker compose down
sudo docker compose pull
sudo docker compose up -d
Verify:
sudo docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
sudo docker logs --tail 20 vaultwarden
Your vault data in vw-data/ stays intact — only the container image changes. 🔄
Backing Up Your Self-Hosted Vaultwarden Password Manager 🗄️
All your data lives in one folder: vw-data/. It contains the SQLite database (db.sqlite3), RSA encryption keys, and attachments. If you lose this folder, your passwords are unrecoverable. ☠️
Create a Backup
Stop the container first to avoid copying a half-written database:
cd /srv/docker/vaultwarden
sudo docker compose down
sudo tar -czf ~/vaultwarden-backup-$(date +%F).tar.gz vw-data
sudo docker compose up -d
Downtime is typically under 30 seconds. ⏱️
Verify
ls -lh ~/vaultwarden-backup-*.tar.gz
tar -tzf ~/vaultwarden-backup-$(date +%F).tar.gz | head -10
Make sure vw-data/db.sqlite3 is listed in the archive.
Restore from Backup 🧯
⚠️ This replaces your current vault. Only do this if your data is broken or lost.
cd /srv/docker/vaultwarden
sudo docker compose down
sudo mv vw-data vw-data.broken.$(date +%F-%H%M)
sudo tar -xzf ~/vaultwarden-backup-YYYY-MM-DD.tar.gz
sudo docker compose up -d
💡 Pro tip: store backup copies on a different physical disk or sync them offsite. If you run OneDrive or Nextcloud on your homelab, drop backups into a synced folder.
Connecting Bitwarden Clients to Your Self-Hosted Vaultwarden Password Manager 📱
The beauty of Vaultwarden is that it works with every official Bitwarden client. Install any of these:
- Browser extension (Chrome, Firefox, Safari, Edge) 🧩
- Mobile app (iOS, Android) 📱
- Desktop app (Windows, macOS, Linux) 💻
Before logging in, tap the ⚙️ gear icon on the login screen and set:
- Server URL:
https://vault.example.com
Then log in with the email and master password you created earlier. Your vault will sync across all devices automatically. 🔄
Final Thoughts ✅
You now have a working self-hosted Vaultwarden password manager with HTTPS, locked-down registration, and a clean update/backup workflow. The whole thing runs in one container and barely touches your server’s resources.
A few reminders:
- Back up before every update — 30 seconds of downtime can save hours of pain 🗄️
- Keep
SIGNUPS_ALLOWEDset tofalseunless you’re actively inviting someone 🔒 - Keep Docker and your host OS updated — your vault is only as secure as the server it runs on 🛡️
- Managing multiple Docker services? Portainer gives you a visual dashboard for all of them 📊
