Vaultwarden on Proxmox LXC: Self-Hosted Password Manager That Never Leaks (2026)
LastPass had its breach. 1Password went to $4/month per family member. Bitwarden is great, but you're still trusting someone else's server. The fix is Vaultwarden β a Rust-rewritten, Bitwarden-compatible server that runs in a Proxmox LXC container, uses under 100MB of RAM, and keeps every credential on hardware you own. This guide walks you through the full setup: LXC creation, Docker Compose deployment, HTTPS with a reverse proxy, admin panel hardening, and a backup strategy that won't let you down. Every command is tested on Proxmox VE 9.
Prerequisites
Before you start, make sure you have the following in place:
- Proxmox VE 8.x or 9.x β commands here are tested on PVE 9. If you're still on 7.x, upgrade first; cgroup v2 changes matter for LXC networking.
- A Debian 12 LXC template downloaded in Proxmox (Storage β debian-12-standard).
- A domain name pointed at your server (or a Cloudflare Tunnel). Vaultwarden requires HTTPS β browsers will not let the web vault load over plain HTTP, and mobile clients won't sync without a valid TLS cert.
- Nginx Proxy Manager or Traefik already running β this guide shows NPM integration. If you want Traefik, check the Traefik on Proxmox LXC guide.
- RAM: 512MB minimum, 1GB recommended for Vaultwarden + Docker overhead. It will technically run on 256MB but under load you'll hit swap.
- Storage: 4GB minimum for the container root. Your vault database will be tiny (under 50MB for most users), but Docker images need space.
Hardware note: Vaultwarden is laughably lightweight β any machine that can run Proxmox can run it. If you're building your first home lab node, the Beelink EQ12 ($169) runs a full 8-LXC stack on 16GB RAM with power draw under 15W idle. Need something that also handles Jellyfin transcoding or Immich ML? Step up to the Beelink SER7 with its Ryzen 7 7840HS.
Step 1 β Create the LXC Container
Log into your Proxmox web UI and click Create CT. Use these settings:
| Field | Value |
|---|---|
| Hostname | vaultwarden |
| Template | debian-12-standard |
| Unprivileged | β (checked) |
| Root disk | 8GB on fast storage |
| CPU cores | 1 (2 if you have headroom) |
| RAM | 512MB |
| Network | vmbr0 bridge, DHCP or a static IP |
| DNS | Leave as host default |
Give the container a static IP from your DHCP server or set it manually in the network tab β you'll need a consistent address to point your reverse proxy at it. I use 192.168.1.50 in this guide.
After creation, open the LXC options and verify that Nesting is enabled under Features β Docker needs it to function inside an LXC. If it's not there, add it:
# From the Proxmox host shell
pct set 100 --features nesting=1Replace 100 with your container ID. Start the container:
pct start 100
pct enter 100Update the base system first:
apt update && apt upgrade -y
apt install -y curl ca-certificates gnupg2Step 2 β Install Docker Inside the LXC
Vaultwarden's official distribution is a Docker image. Installing Docker in a Debian 12 LXC is straightforward with the official repo:
# Add Docker's GPG key
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | \
gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpgAdd Docker repo
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/nullInstall Docker Engine + Compose plugin
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginEnable and start Docker
systemctl enable --now dockerVerify it's working:
docker run --rm hello-worldYou should see "Hello from Docker!" β if you get a cgroups error, double-check that nesting is enabled on the LXC and restart the container.
Step 3 β Deploy Vaultwarden with Docker Compose
Create a dedicated directory for the stack:
mkdir -p /opt/vaultwarden/data
cd /opt/vaultwardenCreate the Compose file:
cat > /opt/vaultwarden/docker-compose.yml << 'EOF'
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: "https://vault.yourdomain.com"
SIGNUPS_ALLOWED: "false"
ADMIN_TOKEN: ""
WEBSOCKET_ENABLED: "true"
LOG_LEVEL: "warn"
volumes:
- ./data:/data
ports:
- "3011:80"
- "3012:3012"
EOFTwo things to configure before starting:
1. Set your domain. Replace vault.yourdomain.com with the actual subdomain you'll use. This is baked into JWT tokens β get it wrong and clients won't authenticate.
2. Generate an ADMIN_TOKEN. This protects the /admin panel. Generate a bcrypt hash (Vaultwarden requires hashed tokens since v1.28):
# Install argon2 or use openssl to generate a random secret first
apt install -y argon2Generate a secure random password (save this β it's your admin password)
ADMIN_PASS=$(openssl rand -base64 48)
echo "Your admin password: $ADMIN_PASS"Hash it with argon2
echo -n "$ADMIN_PASS" | argon2 "$(openssl rand -base64 32)" -e -id -k 65540 -t 3 -p 4Copy the resulting $argon2id$... string and paste it as the ADMIN_TOKEN value in your compose file. Keep your admin password somewhere safe β a text file in your home directory is fine for now since you'll store it in Vaultwarden once it's running.
Start the stack:
cd /opt/vaultwarden
docker compose up -d
docker compose logs -fYou should see Vaultwarden announce it's listening on port 80. Hit Ctrl+C to exit the log tail. The web vault is now accessible at http://192.168.1.50:3011 β but you can't use it over HTTP, so move on to the reverse proxy step.
Step 4 β HTTPS with Nginx Proxy Manager
If you already have NPM running (see the Traefik post if you want that instead), log into its web UI and add a new Proxy Host:
- Domain Names:
vault.yourdomain.com - Forward Hostname / IP:
192.168.1.50 - Forward Port:
3011 - Websockets Support: β enabled (required for real-time sync)
- Block Common Exploits: β enabled
On the SSL tab, request a Let's Encrypt certificate, enable Force SSL and HTTP/2 Support. Save. Within 30 seconds you'll have a valid TLS cert.
Also add a second location entry for the WebSocket endpoint. In the Advanced tab of NPM, add:
location /notifications/hub { proxy_pass http://192.168.1.50:3012; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }
location /notifications/hub/negotiate { proxy_pass http://192.168.1.50:3011; }
This routes WebSocket traffic for real-time vault syncing. Without it, clients poll on a timer instead of pushing changes instantly β works but not ideal.
Navigate to https://vault.yourdomain.com β you should see the Vaultwarden web vault login page with a valid HTTPS lock.
Step 5 β Configuration and Admin Panel
Before creating accounts, visit https://vault.yourdomain.com/admin and log in with your admin password. Review these settings:
General settings:
- Set your actual domain again here (it should be pre-filled).
- Set Invitation expiration to something reasonable (72 hours) if you'll invite family members.
- Enable Emergency Access if you want a trusted contact to be able to request vault access after a waiting period.
Email (SMTP): Vaultwarden sends emails for two-factor auth codes and emergency access. Configure SMTP with your provider β Gmail App Passwords work, or use a transactional service like Mailgun. Without email configured, you can't use 2FA methods that require email delivery.
SMTP example for Gmail:
SMTP Host: smtp.gmail.com
SMTP Port: 587
SMTP Security: STARTTLS
SMTP Username: your@gmail.com
SMTP Password: [App Password β not your Gmail password]
SMTP From: your@gmail.comSave, then use the Test SMTP button. Fix any issues before proceeding β you'll regret not having email when you need to reset 2FA.
Create your first account: Go to the regular web vault (https://vault.yourdomain.com), click Create Account, and register with your email. Then go back to the admin panel and immediately set SIGNUPS_ALLOWED: "false" in your compose file and restart the container:
cd /opt/vaultwarden
docker compose restartOpen registrations only when you need to add a family member or colleague. Leaving signups open exposes your vault server to anyone who finds the URL.
Step 6 β Connect Your Devices
Every official Bitwarden client works with Vaultwarden β they're fully compatible at the API level. The only change is pointing the client at your server instead of Bitwarden's cloud.
Desktop (Windows/macOS/Linux): Download the official Bitwarden desktop app. On the login screen, click the gear icon β Self-hosted environment β enter https://vault.yourdomain.com. Log in normally.
Mobile (iOS/Android): Same flow β tap the gear icon on the login screen before entering credentials. The app will verify your server's TLS cert before connecting.
Browser extensions: Available for Chrome, Firefox, Edge, and Safari. Click the settings gear on the extension login page β Self-hosted environment. Extensions auto-fill credentials and generate 2FA codes without leaving the browser.
Enable 2FA on your account: In the web vault, go to Account Settings β Security β Two-step Login. Enable an authenticator app (Aegis on Android is excellent; Raivo on iOS). Scan the QR code with your authenticator, verify a test code, and save your recovery code somewhere physically safe β a printed paper in your actual safe is not overkill for a password manager.
Post-Install: Backups and Updates
Vaultwarden stores everything in a SQLite database at /opt/vaultwarden/data/db.sqlite3. This file is your entire vault. Losing it means losing every password.
Backup strategy β three layers:
Layer 1 β Proxmox Backup Server snapshots: Configure PBS to snapshot the entire LXC nightly. This gets you point-in-time recovery for the whole container. If you don't have a dedicated backup node yet, a USB-attached drive works. For long-term reliability, a Seagate IronWolf 4TB NAS drive mounted as a PBS datastore gives you years of retention. More on PBS setup: MinIO on Proxmox covers the storage architecture.
Layer 2 β SQLite file backup to your MinIO or a remote location: Add this script to cron:
cat > /opt/vaultwarden/backup.sh << 'EOF'
#!/bin/bash
DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR="/opt/vaultwarden/backups"
DB_FILE="/opt/vaultwarden/data/db.sqlite3"mkdir -p "$BACKUP_DIR"
SQLite safe backup using the .backup command
sqlite3 "$DB_FILE" ".backup $BACKUP_DIR/vault-$DATE.sqlite3"Compress it
gzip "$BACKUP_DIR/vault-$DATE.sqlite3"Keep only the last 30 days
find "$BACKUP_DIR" -name "*.gz" -mtime +30 -deleteecho "Backup complete: vault-$DATE.sqlite3.gz"
EOF
chmod +x /opt/vaultwarden/backup.sh
Add to cron β runs at 2 AM daily
(crontab -l 2>/dev/null; echo "0 2 * /opt/vaultwarden/backup.sh >> /var/log/vaultwarden-backup.log 2>&1") | crontab -Layer 3 β Bitwarden's encrypted export: Periodically export your vault from the web UI (Tools β Export Vault) as an encrypted JSON. Store this on a USB drive or in cloud storage. If your entire Proxmox node burns, you can restore from this into any Bitwarden-compatible server.
Updates: Vaultwarden publishes releases frequently. Update with:
cd /opt/vaultwarden
docker compose pull
docker compose up -dCheck the Vaultwarden releases page before updating β breaking changes are rare but documented. Pulling latest is generally safe but pinning a version tag in your compose file gives you more control.
Reverse proxy with Traefik: If you're running the Traefik setup from the Traefik guide, you can add Vaultwarden as a Docker label-routed service instead of NPM. The setup is similar but uses Traefik's YAML or label-based config β worth doing if you have 10+ services to manage.
Hardware for a capable home lab node: If you're scaling up beyond a single-service setup, the Beelink SER7 handles a dozen LXC containers comfortably. Pair it with a Crucial P3 Plus 1TB NVMe for your Proxmox root and a Samsung 870 EVO 1TB SATA SSD as a secondary datastore for LXC roots. The Beelink EQ12 is the budget pick if Vaultwarden plus 3-4 other light services is your entire stack.
For networking, a TP-Link TL-SG108E 8-Port managed switch lets you VLAN-isolate your homelab traffic so Vaultwarden lives on a separate segment from your IoT devices β good security hygiene when you're running a password manager.
Security Hardening Checklist
A password manager is a high-value target. The default Vaultwarden setup is functional but not hardened β run through this checklist before pointing devices at it.
Rate-limit login attempts. Vaultwarden has built-in rate limiting, but adding fail2ban in front of your reverse proxy adds another layer. Install fail2ban in your NPM container or on the Proxmox host and add a filter that watches for 401 responses from your vault subdomain. Five failed logins in ten minutes should trigger a 10-minute ban by IP.
# fail2ban filter for Vaultwarden (save as /etc/fail2ban/filter.d/vaultwarden.conf)
[Definition]
failregex = ^.Username or password is incorrect\. Try again\. IP: <ADDR>\..$
^.FAILED login attempt for . from IP <ADDR>\..*$
ignoreregex =# jail definition (/etc/fail2ban/jail.d/vaultwarden.local)
[vaultwarden]
enabled = true
port = http,https
filter = vaultwarden
logpath = /opt/vaultwarden/data/vaultwarden.log
maxretry = 5
bantime = 600
findtime = 600Keep the admin panel off the public internet. Your /admin endpoint has an ADMIN_TOKEN protecting it, but why leave it exposed? Add an IP allowlist in NPM's Advanced config:
location /admin {
allow 192.168.1.0/24;
deny all;
proxy_pass http://192.168.1.50:3011;
}Now the admin panel is only reachable from your LAN. Access it remotely via Tailscale or Headscale when needed.
Disable user registration invitations for unused features. If you're solo on this vault, disable INVITATIONS_ALLOWED in your environment too:
INVITATIONS_ALLOWED: "false"
EMERGENCY_ACCESS_ALLOWED: "true" # keep this for account recoveryEnable Duo or Yubikey 2FA for admin accounts. In the admin panel, Vaultwarden supports TOTP, Duo, email OTP, FIDO2/Passkeys, and YubiKey. TOTP via Aegis (Android) or Raivo (iOS) is the baseline. If you have a YubiKey 5 NFC, adding hardware 2FA is the strongest option β it requires the physical key even if your master password is compromised.
Firewall the LXC directly. Proxmox's Firewall tab on each LXC lets you add rules at the hypervisor level. Allow only port 3011 and 3012 inbound from your NPM container's IP, and block everything else. This limits blast radius if something else on the same network gets compromised.
Honest Assessment: When NOT to Self-Host Your Password Manager
Vaultwarden is rock-solid, but password management is where honest trade-offs really matter.
Self-host Vaultwarden if: You have a reliable home lab with UPS protection and multiple backup layers. You're comfortable maintaining a server and keeping it updated. You understand that your passwords are now only as safe as your Proxmox node's physical and network security.
Don't self-host if: Your home lab runs on a machine without UPS protection and your power is flaky. You don't have a tested restore procedure for your vault. You share the vault with family members who have no tolerance for downtime β a 2 AM hardware failure means no one can log into anything until you fix it. You're not confident you'll stay on top of security updates. Vaultwarden has had security patches; being weeks behind on updates for a password manager is a real risk.
The honest middle ground: Many users run Vaultwarden self-hosted as their primary vault but maintain Bitwarden cloud as a paid secondary account with a read-only export. You get full control with a fallback if your home lab goes dark for an extended period.
If you want a managed cloud option with a permissive free tier and an open-source server you could self-host later, the official Bitwarden cloud is $0/year for personal use and the company undergoes third-party security audits. It's a respectable choice.
FAQ
Can I migrate from 1Password or LastPass to Vaultwarden?
Yes. Both apps can export a CSV or JSON that you import into Bitwarden's web vault (Tools β Import Data). Select your source format, upload the file, and your entire vault imports in seconds. 1Password's .1pux format is the cleanest β use it over CSV if you can.
Is Vaultwarden actually secure? It's unofficial.
Vaultwarden doesn't implement cryptography β it's a server that handles encrypted blobs. The actual encryption (AES-256-CBC with PBKDF2 or Argon2id key derivation) happens in the official Bitwarden clients, which are open source and audited. Your vault is encrypted with your master password before it ever leaves your device. The server, including Vaultwarden, only ever sees ciphertext. The Vaultwarden project has been running since 2018, has 45k+ GitHub stars, and has an active security response process. It's as secure as your infrastructure and your master password.
Do the Bitwarden browser extensions work with Vaultwarden?
Yes, fully. All official Bitwarden clients β browser extensions for Chrome/Firefox/Edge/Safari, desktop apps for Windows/macOS/Linux, and mobile apps for iOS and Android β work against Vaultwarden with no modification. You just point them at your server URL on the login screen.
How much RAM does Vaultwarden actually use?
The Vaultwarden process itself uses 10β30MB of RSS. Docker's containerd overhead adds another 50MB. So realistically: 80β100MB for the full stack on a container with no other services. That's why a 512MB LXC is more than sufficient. If you're very constrained, you can run it with 256MB and it'll work β you'll just have almost no headroom.
What's the difference between Vaultwarden and Bitwarden's official self-hosted server?
Bitwarden's official self-host package runs on .NET Core and requires a stack that includes multiple services, SQL Server or PostgreSQL, and around 2GB of RAM minimum. It's designed for organizations. Vaultwarden is a Rust reimplementation of the same API surface, single-binary, SQLite by default, and runs in 100MB. For personal and family use, Vaultwarden is the clear choice. The official server adds things like directory sync and policies for enterprise deployments β most homelabbers don't need any of that.
Can I expose Vaultwarden to the internet?
Yes, and you need to for mobile sync unless you're on Tailscale/Headscale all the time. Use a domain with Let's Encrypt TLS, fail2ban or Crowdsec to rate-limit login attempts, and enable 2FA on every account. If you're uncomfortable with public exposure, route through a Cloudflare Tunnel β no inbound ports required.