Self-Hosted Password Manager: Vaultwarden on Proxmox
By LK Wood IV · 2026-06-13 · ~14 min read · St. Louis County, MO
Password managers that live in someone else’s cloud have one failure mode that self-hosted doesn’t: the company goes under, gets acquired, or changes pricing. Vaultwarden on Proxmox solves this. Your vault lives on hardware you own, all Bitwarden official clients work against it without modification, and the whole setup runs in a lightweight LXC container that uses maybe 80MB of RAM.
This guide goes from zero to a working Vaultwarden install with HTTPS, automatic backups, and all your devices connected.
What Vaultwarden is
Vaultwarden is an open-source reimplementation of the Bitwarden server written in Rust. It is not affiliated with Bitwarden, Inc., but it is compatible with all official Bitwarden clients — browser extensions, mobile apps, desktop apps — without any modification to those clients.
What you get versus bitwarden.com paid plans: organizations, collections, TOTP authenticator codes, and Bitwarden Send — all for free, running on your own hardware. What you don’t get: Bitwarden’s enterprise compliance features and their guaranteed uptime. For a homelab password manager, the trade is worth it.
Prerequisites
- Proxmox VE 8.x with LXC capability
- A domain name you control with the ability to add a DNS record (even a subdomain works)
- Tailscale or port forwarding to reach your homelab from outside (for syncing mobile devices)
- Basic comfort with SSH and running commands
For the HTTPS setup, this guide uses Caddy as the reverse proxy because its automatic Let’s Encrypt handling requires zero certificate management on your part. If you already have nginx handling reverse proxy, adapt the config blocks to nginx.
Step 1: Create a Debian LXC in Proxmox
In the Proxmox web UI:
- Create CT → Debian 12 template (download from Proxmox if not already present)
- Hostname:
vaultwarden - Storage: 8GB is plenty (Vaultwarden + OS is under 500MB; increase if you’ll store file attachments)
- RAM: 256MB minimum, 512MB comfortable
- CPU: 1 core is sufficient
- Network: DHCP or a static IP on your LAN
- Features → Nesting: enable if you want Docker later; not required for this setup
Start the container and SSH in as root, or use the Proxmox console.
Update the system:
apt update && apt upgrade -y
apt install -y curl wget unzip
Step 2: Install Vaultwarden
Vaultwarden ships as a single binary with no external dependencies beyond a SQLite database. The simplest install method is the official Docker image, but since we’re in an LXC and want minimal overhead, we’ll use the pre-built binary distribution.
Option A: Docker Compose (easiest, ~5MB extra overhead)
Install Docker:
curl -fsSL https://get.docker.com | sh
Create a directory and compose file:
mkdir -p /opt/vaultwarden/data
cd /opt/vaultwarden
# /opt/vaultwarden/docker-compose.yml
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
volumes:
- ./data:/data
environment:
DOMAIN: "https://vault.yourdomain.com"
SIGNUPS_ALLOWED: "false"
ADMIN_TOKEN: "generate-a-strong-token-here"
ports:
- "127.0.0.1:8080:80"
Generate a strong admin token:
openssl rand -base64 48
Paste the output as ADMIN_TOKEN. Set DOMAIN to the URL you’ll access Vaultwarden at.
Start it:
docker compose up -d
docker compose logs -f
Vaultwarden is now listening on 127.0.0.1:8080. The reverse proxy (next step) exposes it publicly over HTTPS.
Option B: Pre-built binary (no Docker)
Download the latest release from github.com/dani-garcia/vaultwarden/releases. Get the amd64-musl tarball:
wget https://github.com/dani-garcia/vaultwarden/releases/latest/download/vaultwarden-1.32.7-linux-amd64.tar.gz
tar -xzf vaultwarden-*.tar.gz
mv vaultwarden /usr/local/bin/
chmod +x /usr/local/bin/vaultwarden
Create a data directory and .env file:
mkdir -p /opt/vaultwarden/data
# /opt/vaultwarden/.env
DATA_FOLDER=/opt/vaultwarden/data
DOMAIN=https://vault.yourdomain.com
SIGNUPS_ALLOWED=false
ADMIN_TOKEN=your-strong-token-here
ROCKET_ADDRESS=127.0.0.1
ROCKET_PORT=8080
Create a systemd service:
# /etc/systemd/system/vaultwarden.service
[Unit]
Description=Vaultwarden password server
After=network.target
[Service]
User=root
EnvironmentFile=/opt/vaultwarden/.env
ExecStart=/usr/local/bin/vaultwarden
Restart=on-failure
WorkingDirectory=/opt/vaultwarden
[Install]
WantedBy=multi-user.target
systemctl enable --now vaultwarden
systemctl status vaultwarden
Step 3: HTTPS with Caddy
Install Caddy:
apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update
apt install -y caddy
Create the Caddyfile:
# /etc/caddy/Caddyfile
vault.yourdomain.com {
reverse_proxy localhost:8080
}
Replace vault.yourdomain.com with your actual domain. Caddy automatically obtains a Let’s Encrypt certificate for this domain — no certbot, no manual renewal.
For Caddy to get the certificate, port 80 and 443 on your router must forward to this LXC’s LAN IP. If you’re using Tailscale for access only (not exposing to the internet), skip Let’s Encrypt and use Caddy with a self-signed cert or use the Tailscale HTTPS feature instead.
systemctl enable --now caddy
systemctl status caddy
After Caddy starts, wait 30–60 seconds for certificate issuance, then visit https://vault.yourdomain.com. You should see the Bitwarden login screen.
Step 4: Create your account and disable signups
Open https://vault.yourdomain.com and create your account. After your account is created, signups are already disabled in the config (SIGNUPS_ALLOWED=false). New users would need to go through the admin panel at https://vault.yourdomain.com/admin to create accounts.
The admin panel requires the ADMIN_TOKEN you set. Go there now and confirm the settings look right.
Enable TOTP 2FA. In your Vaultwarden account settings → Security → Two-step login → enable Authenticator App. Store the recovery codes somewhere safe — not in Vaultwarden itself (obvious), not in your email (bad idea), somewhere physically separate.
Step 5: Connect all devices
Browser extension: Install the Bitwarden extension for Chrome, Firefox, or Safari. Click the extension → Settings → Self-Hosted Environment. Set Server URL to https://vault.yourdomain.com. Log in with your account.
Mobile (iOS/Android): Open the Bitwarden app → Settings → Self-Hosted → Server URL. Same URL. Log in. On iOS, go to Settings → Passwords → Password Options → enable Bitwarden for AutoFill.
Desktop app: Same flow — Settings → Self-Hosted.
After each client connects and syncs, disable bitwarden.com as an option in the extension settings (under the account menu) so you don’t accidentally log into the cloud version.
Step 6: Automated backups
Vaultwarden stores everything in /opt/vaultwarden/data/ (or /opt/vaultwarden/data for the Docker setup where it’s a volume at ./data). The critical files:
db.sqlite3— your entire vault databaseattachments/— any file attachmentsconfig.json— server configuration (mostly redundant with your .env, but include it)rsa_key*— RSA keys for JWT signing (back these up or sessions will invalidate after a restore)
Create a backup script:
#!/bin/bash
# /opt/vaultwarden/backup.sh
BACKUP_DIR=/mnt/nas-backups/vaultwarden
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
DEST="$BACKUP_DIR/vw-backup-$TIMESTAMP"
mkdir -p "$DEST"
# Use SQLite's online backup API to avoid corruption during a live backup
sqlite3 /opt/vaultwarden/data/db.sqlite3 ".backup '$DEST/db.sqlite3'"
cp -r /opt/vaultwarden/data/attachments "$DEST/" 2>/dev/null || true
cp /opt/vaultwarden/data/rsa_key* "$DEST/"
# Keep only 30 days of backups
find "$BACKUP_DIR" -name "vw-backup-*" -mtime +30 -exec rm -rf {} +
echo "Backup complete: $DEST"
chmod +x /opt/vaultwarden/backup.sh
# Add to crontab - daily at 2am
echo "0 2 * * * root /opt/vaultwarden/backup.sh >> /var/log/vw-backup.log 2>&1" >> /etc/crontab
Replace /mnt/nas-backups/vaultwarden with your NAS mount point or a path that gets swept by Proxmox Backup Server.
Critical: test the restore before you need it. Stop Vaultwarden, replace db.sqlite3 with a backup copy, start Vaultwarden, verify your passwords are there, then restore the production database. Takes 5 minutes and confirms your backup is valid.
Step 7: Tailscale-only access (no public exposure)
If you don’t want Vaultwarden reachable from the public internet — only accessible while on your tailnet — skip the Let’s Encrypt setup and use Tailscale HTTPS instead.
Tailscale provides valid HTTPS certificates for your tailnet nodes via tailscale cert. On the Vaultwarden container (with Tailscale installed):
tailscale cert vaultwarden.your-tailnet.ts.net
This generates a certificate at /var/lib/tailscale/certs/. Update your Caddyfile to use it:
vaultwarden.your-tailnet.ts.net {
tls /var/lib/tailscale/certs/vaultwarden.your-tailnet.ts.net.crt \
/var/lib/tailscale/certs/vaultwarden.your-tailnet.ts.net.key
reverse_proxy localhost:8080
}
Now Vaultwarden is only reachable when you’re connected to your tailnet. No public port exposure. Mobile Bitwarden clients sync when you’re on tailnet (at home or connected via Tailscale from anywhere). This is the highest-security configuration for a homelab password manager.
Updating Vaultwarden
For Docker Compose:
cd /opt/vaultwarden
docker compose pull
docker compose up -d
For the binary:
systemctl stop vaultwarden
# Download new binary, replace /usr/local/bin/vaultwarden
systemctl start vaultwarden
Vaultwarden runs automatic database migrations on startup. Back up before updating regardless.
Resource usage
On an idle Debian 12 LXC running Vaultwarden (Docker, 3 users, 1200 vault items):
- RAM: ~85MB for Vaultwarden + ~15MB for Caddy = ~100MB total container RAM
- CPU: <1% idle, brief spikes on sync
- Disk: ~45MB for database + OS, excluding attachments
- Network: negligible except during initial sync
This is a workload that runs comfortably alongside a dozen other services on a mini PC with 16GB RAM. It does not need its own machine. The Power & Cost Calculator shows adding an LXC like this costs essentially nothing in electricity — the container overhead is measured in milliwatts.
Running Vaultwarden and other self-hosted services on Proxmox? The LXC vs VM guide covers when to use each. Tailscale setup handles the remote access piece — sync your Bitwarden clients from anywhere without exposing ports. If you prefer to give Vaultwarden a clean vault.yourdomain.com address without a dedicated Caddy instance, Nginx Proxy Manager centralizes SSL termination across all your services.