Self-Hosted Password Management: Vaultwarden and Passbolt
Your password manager is arguably the most important piece of software you use. It holds the keys to everything — bank accounts, email, social media, infrastructure. Trusting that to a cloud provider means trusting that they won't get breached, won't change their pricing, won't sunset the product, and won't lock you out.
Self-hosting your password manager removes that trust dependency. You own the data, you control the encryption, and you decide when updates happen. The trade-off is that you're now responsible for keeping it running, backed up, and secure. But if you're running a homelab, you're already signing up for that responsibility.
This guide covers two self-hosted options: Vaultwarden (a Bitwarden-compatible server that's lightweight and feature-rich) and Passbolt (a team-focused alternative designed for sharing credentials). We'll go deep on deployment, hardening, backup, and migration.
Why Self-Host Your Passwords?
Let's be honest about the trade-offs:
Advantages:
- Complete data sovereignty — your vault never leaves your network
- No subscription fees (Bitwarden premium features are free in Vaultwarden)
- No vendor lock-in — standard export formats
- You control the update schedule
- Full audit logging
Disadvantages:
- You are the SRE — if it goes down at 2 AM, that's your problem
- Security responsibility is on you (updates, hardening, backups)
- Need reliable infrastructure (if your server dies and you have no backup, you lose everything)
- Mobile access requires exposing the service to the internet (or using a VPN)
The golden rule: If you self-host your password manager, your backup strategy must be airtight. This is the one service where "I'll set up backups later" is genuinely dangerous.
Vaultwarden: The Bitwarden-Compatible Server
Vaultwarden (formerly bitwarden_rs) is an unofficial Bitwarden server implementation written in Rust. It's compatible with all official Bitwarden clients (browser extensions, desktop apps, mobile apps) but uses far fewer resources than the official server.
Why Vaultwarden Instead of Official Bitwarden?
| Feature | Official Bitwarden | Vaultwarden |
|---|---|---|
| RAM usage | 2-4 GB (multiple containers) | 50-100 MB |
| Containers | 6+ (MSSQL, API, Identity, etc.) | 1 |
| Premium features | $10/year subscription | Free (all features) |
| Organizations | Free (limited), paid tiers | Free (all features) |
| Send feature | Premium | Free |
| Emergency access | Premium | Free |
| Database | MSSQL (heavy) | SQLite or PostgreSQL |
| Maintenance | More complex | Simple |
For a homelab, Vaultwarden is the obvious choice. It's lighter, simpler, and gives you all the premium features without a subscription.
Docker Deployment with SQLite
The simplest deployment — good for personal use or small households:
# docker-compose.yml
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
# Domain — MUST match your actual URL
DOMAIN: "https://vault.homelab.example.com"
# Admin panel (generate a strong token)
ADMIN_TOKEN: "${ADMIN_TOKEN}"
# Signup settings
SIGNUPS_ALLOWED: "false" # Disable after creating your account
INVITATIONS_ALLOWED: "true" # Allow invites from existing users
# SMTP for email (required for 2FA recovery, invites)
SMTP_HOST: "smtp.fastmail.com"
SMTP_FROM: "[email protected]"
SMTP_PORT: 587
SMTP_SECURITY: "starttls"
SMTP_USERNAME: "${SMTP_USERNAME}"
SMTP_PASSWORD: "${SMTP_PASSWORD}"
# Security settings
PASSWORD_ITERATIONS: 600000 # PBKDF2 iterations (OWASP minimum)
LOGIN_RATELIMIT_MAX_BURST: 5
LOGIN_RATELIMIT_SECONDS: 60
# WebSocket (for live sync)
WEBSOCKET_ENABLED: "true"
# Logging
LOG_LEVEL: "info"
EXTENDED_LOGGING: "true"
volumes:
- ./vw-data:/data
ports:
- "127.0.0.1:8080:80" # Only bind to localhost (reverse proxy in front)
- "127.0.0.1:3012:3012" # WebSocket
# Generate a secure admin token
openssl rand -base64 48
# Put it in a .env file next to docker-compose.yml:
# ADMIN_TOKEN=your-generated-token
# SMTP_USERNAME=your-email
# SMTP_PASSWORD=your-app-password
# Start Vaultwarden
docker compose up -d
# Check logs
docker compose logs -f vaultwarden
Docker Deployment with PostgreSQL
For multi-user setups or if you want better concurrency:
# docker-compose.yml
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
DOMAIN: "https://vault.homelab.example.com"
DATABASE_URL: "postgresql://vaultwarden:${DB_PASSWORD}@db:5432/vaultwarden"
ADMIN_TOKEN: "${ADMIN_TOKEN}"
SIGNUPS_ALLOWED: "false"
INVITATIONS_ALLOWED: "true"
SMTP_HOST: "smtp.fastmail.com"
SMTP_FROM: "[email protected]"
SMTP_PORT: 587
SMTP_SECURITY: "starttls"
SMTP_USERNAME: "${SMTP_USERNAME}"
SMTP_PASSWORD: "${SMTP_PASSWORD}"
PASSWORD_ITERATIONS: 600000
LOGIN_RATELIMIT_MAX_BURST: 5
LOGIN_RATELIMIT_SECONDS: 60
WEBSOCKET_ENABLED: "true"
volumes:
- vw-data:/data
ports:
- "127.0.0.1:8080:80"
- "127.0.0.1:3012:3012"
db:
image: postgres:16-alpine
container_name: vaultwarden-db
restart: unless-stopped
environment:
POSTGRES_USER: vaultwarden
POSTGRES_PASSWORD: "${DB_PASSWORD}"
POSTGRES_DB: vaultwarden
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U vaultwarden"]
interval: 10s
timeout: 5s
retries: 5
volumes:
vw-data:
db-data:
HTTPS with Nginx Reverse Proxy
Never expose Vaultwarden without HTTPS. Your master password is transmitted during login.
# /etc/nginx/sites-available/vaultwarden
server {
listen 443 ssl http2;
server_name vault.homelab.example.com;
# TLS certificates (Let's Encrypt or self-signed)
ssl_certificate /etc/letsencrypt/live/vault.homelab.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vault.homelab.example.com/privkey.pem;
# Strong TLS settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS
add_header Strict-Transport-Security "max-age=63072000" always;
# Security headers
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
# Max upload size (for attachments)
client_max_body_size 525M;
# Main Vaultwarden proxy
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;
}
# WebSocket proxy (for live sync)
location /notifications/hub {
proxy_pass http://127.0.0.1:3012;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# Admin panel — restrict to local network
location /admin {
# Only allow from LAN
allow 192.168.1.0/24;
allow 10.0.0.0/8;
deny all;
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;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name vault.homelab.example.com;
return 301 https://$server_name$request_uri;
}
# Enable the site
sudo ln -s /etc/nginx/sites-available/vaultwarden /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Caddy Alternative (Simpler)
If you prefer Caddy (automatic HTTPS):
# /etc/caddy/Caddyfile
vault.homelab.example.com {
# Reverse proxy to Vaultwarden
reverse_proxy /notifications/hub 127.0.0.1:3012
reverse_proxy 127.0.0.1:8080 {
header_up X-Real-IP {remote_host}
}
# Restrict admin panel
@admin path /admin*
handle @admin {
@blocked not remote_ip 192.168.1.0/24 10.0.0.0/8
respond @blocked 403
reverse_proxy 127.0.0.1:8080
}
}
Initial Setup
- Start Vaultwarden and navigate to
https://vault.homelab.example.com - Create your account (this is your master account)
- Immediately disable signups by setting
SIGNUPS_ALLOWED=falseand restarting - Access the admin panel at
/adminto configure additional settings - Set up 2FA on your account (TOTP or hardware key)
Admin Panel Configuration
Navigate to https://vault.homelab.example.com/admin and configure:
General Settings:
- Domain URL: https://vault.homelab.example.com
- Signups allowed: false
- Invitations allowed: true (for family members)
SMTP Settings:
- Verify email on signup: true
- Test the SMTP connection
Advanced Settings:
- Disable icon downloads: false (useful, but leaks which sites you use to icon servers)
- Password iterations: 600000
Organizations and Sharing
Vaultwarden supports Bitwarden Organizations, which let you share passwords with family or team members:
1. In the web vault, go to Organizations > New Organization
2. Name it (e.g., "Family")
3. Create Collections (e.g., "Streaming", "Shared Accounts", "Home Network")
4. Invite family members (they'll get an email)
5. Assign items to collections
Each collection has its own access control — you can give family members read-only access to WiFi passwords while giving full access to shared streaming accounts.
Browser Extension and Mobile App Setup
Browser Extensions
- Install the Bitwarden extension from your browser's extension store (Chrome, Firefox, Safari, Edge)
- Click the extension icon > Settings (gear)
- Under "Self-hosted environment", enter:
- Server URL:
https://vault.homelab.example.com
- Server URL:
- Save and log in with your credentials
Mobile Apps
- Install Bitwarden from the App Store (iOS) or Play Store (Android)
- On the login screen, tap the region selector (or settings gear)
- Choose "Self-hosted" and enter:
- Server URL:
https://vault.homelab.example.com
- Server URL:
- Log in
Important: Your Vaultwarden instance must be accessible from the internet for mobile access outside your home. Options:
- Cloudflare Tunnel (recommended — no port forwarding needed)
- Reverse proxy with port forwarding (open port 443)
- VPN to your homelab first (most secure, but less convenient)
Cloudflare Tunnel for External Access
# If you have cloudflared installed and configured
cloudflared tunnel route dns homelab-tunnel vault.homelab.example.com
# Add to your tunnel config
# ~/.cloudflared/config.yml
ingress:
- hostname: vault.homelab.example.com
service: http://127.0.0.1:8080
- service: http_status:404
Passbolt: Team-Focused Password Sharing
If your primary need is sharing credentials with a team (home IT, a small business, a friend group managing shared infrastructure), Passbolt is worth considering. It's built around the concept of team password management with GPG-based end-to-end encryption.
Passbolt vs Vaultwarden
| Feature | Vaultwarden | Passbolt CE |
|---|---|---|
| Primary use | Personal + sharing | Team sharing |
| Encryption | AES-256 (master password) | GPG (per-user keys) |
| Browser extension | Bitwarden (polished) | Passbolt (functional) |
| Mobile app | Bitwarden (excellent) | Passbolt (limited) |
| Sharing model | Organizations + Collections | Groups + Folders |
| Password generator | Built-in | Built-in |
| TOTP/2FA storage | Yes (premium free) | No (Pro feature) |
| Emergency access | Yes | No |
| Resource usage | ~50-100 MB RAM | ~500 MB RAM (PHP + MariaDB) |
| Complexity | Simple | Moderate |
Passbolt Docker Deployment
# docker-compose.yml
services:
passbolt:
image: passbolt/passbolt:latest-ce
container_name: passbolt
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
APP_FULL_BASE_URL: "https://pass.homelab.example.com"
DATASOURCES_DEFAULT_HOST: "db"
DATASOURCES_DEFAULT_USERNAME: "passbolt"
DATASOURCES_DEFAULT_PASSWORD: "${DB_PASSWORD}"
DATASOURCES_DEFAULT_DATABASE: "passbolt"
EMAIL_TRANSPORT_DEFAULT_HOST: "smtp.fastmail.com"
EMAIL_TRANSPORT_DEFAULT_PORT: 587
EMAIL_TRANSPORT_DEFAULT_TLS: "true"
EMAIL_DEFAULT_FROM: "[email protected]"
EMAIL_TRANSPORT_DEFAULT_USERNAME: "${SMTP_USERNAME}"
EMAIL_TRANSPORT_DEFAULT_PASSWORD: "${SMTP_PASSWORD}"
volumes:
- gpg-data:/etc/passbolt/gpg
- jwt-data:/etc/passbolt/jwt
ports:
- "127.0.0.1:8443:443"
- "127.0.0.1:8080:80"
db:
image: mariadb:11
container_name: passbolt-db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}"
MYSQL_DATABASE: passbolt
MYSQL_USER: passbolt
MYSQL_PASSWORD: "${DB_PASSWORD}"
volumes:
- db-data:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
volumes:
gpg-data:
jwt-data:
db-data:
# Start Passbolt
docker compose up -d
# Create the first admin user
docker compose exec passbolt su -m -c \
"/usr/share/php/passbolt/bin/cake passbolt register_user \
-u [email protected] \
-f Admin \
-l User \
-r admin" -s /bin/sh www-data
# This outputs a URL — open it in your browser to complete setup
TOTP/2FA Integration
Vaultwarden can store your TOTP (Time-based One-Time Password) codes alongside your passwords. This is a premium Bitwarden feature that's free in Vaultwarden.
Setting Up TOTP in Vaultwarden
- Edit a vault entry
- Click "TOTP" field
- Enter the TOTP secret key (or scan the QR code with the mobile app)
- The browser extension and mobile app will auto-fill TOTP codes
Security consideration: Storing TOTP codes in the same vault as passwords reduces the "second factor" to "second thing in the same place." For high-security accounts (email, banking), consider using a separate TOTP app (like Aegis or Raivo) that's not in your password vault.
Hardware Key Support
Vaultwarden supports FIDO2/WebAuthn hardware keys (YubiKey, SoloKey) for vault login:
- Log into the web vault
- Go to Settings > Security > Two-step Login
- Enable "FIDO2 WebAuthn" and register your key
- The key will be required for every vault unlock
This is the strongest second factor — even if someone gets your master password, they can't unlock the vault without the physical key.
Emergency Access
Vaultwarden supports Bitwarden's Emergency Access feature, which lets a trusted contact access your vault if you're incapacitated.
Setting Up Emergency Access
- In the web vault, go to Settings > Emergency Access
- Click "Invite emergency contact"
- Enter their email and choose access level:
- View: They can see (but not edit) your vault
- Takeover: They get full access to your vault
- Set a wait time (1-90 days): After requesting access, they must wait this long. During the wait, you receive email notifications and can deny the request.
Recommended setup:
- Trusted family member: View access, 7-day wait
- Spouse/partner: Takeover access, 3-day wait
This is one of the most underused features. If you get hit by a bus, can your family access your accounts? Emergency access solves this.
Backup and Restore
This is critical. Your password vault is arguably the most important data in your homelab.
Backup Strategy for Vaultwarden (SQLite)
#!/bin/bash
# backup-vaultwarden.sh
BACKUP_DIR="/mnt/backups/vaultwarden"
VW_DATA="/opt/vaultwarden/vw-data"
DATE=$(date +%Y%m%d-%H%M%S)
mkdir -p "$BACKUP_DIR"
# Stop the container briefly for a consistent backup
# (or use sqlite3 .backup for online backup)
docker compose -f /opt/vaultwarden/docker-compose.yml stop vaultwarden
# Backup the entire data directory
tar czf "$BACKUP_DIR/vaultwarden-$DATE.tar.gz" \
-C "$VW_DATA" .
# Restart
docker compose -f /opt/vaultwarden/docker-compose.yml start vaultwarden
# Online backup alternative (no downtime):
# sqlite3 "$VW_DATA/db.sqlite3" ".backup '$BACKUP_DIR/db-$DATE.sqlite3'"
# Keep only last 30 backups
ls -t "$BACKUP_DIR"/vaultwarden-*.tar.gz | tail -n +31 | xargs rm -f
echo "Backup completed: $BACKUP_DIR/vaultwarden-$DATE.tar.gz"
Backup Strategy for Vaultwarden (PostgreSQL)
#!/bin/bash
# backup-vaultwarden-pg.sh
BACKUP_DIR="/mnt/backups/vaultwarden"
DATE=$(date +%Y%m%d-%H%M%S)
mkdir -p "$BACKUP_DIR"
# Database dump
docker compose exec -T db pg_dump -U vaultwarden vaultwarden \
| gzip > "$BACKUP_DIR/db-$DATE.sql.gz"
# Attachments and config
tar czf "$BACKUP_DIR/vw-data-$DATE.tar.gz" \
-C /opt/vaultwarden/vw-data \
--exclude='db.*' .
# Keep only last 30 backups
ls -t "$BACKUP_DIR"/db-*.sql.gz | tail -n +31 | xargs rm -f
ls -t "$BACKUP_DIR"/vw-data-*.tar.gz | tail -n +31 | xargs rm -f
echo "Backup completed: $DATE"
Automated Backup with systemd Timer
# /etc/systemd/system/vaultwarden-backup.service
[Unit]
Description=Vaultwarden Backup
After=docker.service
[Service]
Type=oneshot
ExecStart=/opt/vaultwarden/backup-vaultwarden.sh
User=root
# /etc/systemd/system/vaultwarden-backup.timer
[Unit]
Description=Daily Vaultwarden Backup
[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=900
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl enable --now vaultwarden-backup.timer
Offsite Backup
Local backups aren't enough. If your house floods or your server dies, you need offsite copies:
# Encrypt and upload to a cloud storage provider
gpg --symmetric --cipher-algo AES256 \
"$BACKUP_DIR/vaultwarden-$DATE.tar.gz"
# Upload to Backblaze B2
b2 upload-file my-vault-backups \
"$BACKUP_DIR/vaultwarden-$DATE.tar.gz.gpg" \
"vaultwarden/$DATE.tar.gz.gpg"
# Or rsync to a remote server
rsync -az --progress \
"$BACKUP_DIR/vaultwarden-$DATE.tar.gz" \
backup-server:/mnt/offsite/vaultwarden/
Restore Procedure
# Stop Vaultwarden
docker compose down
# Restore from backup
rm -rf /opt/vaultwarden/vw-data/*
tar xzf /mnt/backups/vaultwarden/vaultwarden-LATEST.tar.gz \
-C /opt/vaultwarden/vw-data/
# Start Vaultwarden
docker compose up -d
# Verify by logging in
curl -s https://vault.homelab.example.com/api/alive
Test your restores regularly. A backup you've never tested is not a backup.
Security Hardening
Fail2ban for Brute Force Protection
# /etc/fail2ban/filter.d/vaultwarden.conf
[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
ignoreregex =
# /etc/fail2ban/jail.d/vaultwarden.local
[vaultwarden]
enabled = true
port = http,https
filter = vaultwarden
action = %(action_mwl)s
logpath = /opt/vaultwarden/vw-data/vaultwarden.log
maxretry = 5
bantime = 14400
findtime = 14400
sudo systemctl restart fail2ban
sudo fail2ban-client status vaultwarden
Admin Panel Security
The admin panel should not be accessible from the internet:
# Option 1: Disable the admin panel entirely after initial setup
environment:
ADMIN_TOKEN: "" # Empty string disables admin panel
# Option 2: Use an Argon2 hashed token (recommended)
# Generate with: echo -n "your-admin-password" | argon2 "$(openssl rand -base64 32)" -e -id -k 65540 -t 3 -p 4
environment:
ADMIN_TOKEN: "$argon2id$v=19$m=65540,t=3,p=4$..."
Rate Limiting
Vaultwarden has built-in rate limiting:
environment:
LOGIN_RATELIMIT_SECONDS: 60
LOGIN_RATELIMIT_MAX_BURST: 5
ADMIN_RATELIMIT_SECONDS: 60
ADMIN_RATELIMIT_MAX_BURST: 3
Network Segmentation
If possible, run Vaultwarden on a dedicated VLAN or network segment:
Internet → Reverse Proxy (DMZ) → Vaultwarden (Trusted VLAN)
↓
Database (Internal only)
Migration from Cloud Services
From Cloud Bitwarden
If you're moving from Bitwarden's cloud to Vaultwarden:
- Log into
vault.bitwarden.com - Go to Tools > Export Vault
- Choose format: Encrypted JSON (recommended) or JSON
- Export and save the file
- Log into your Vaultwarden instance
- Go to Tools > Import Data
- Select "Bitwarden (json)" and upload your export
From LastPass
1. Log into lastpass.com
2. Advanced Options > Export
3. Save the CSV file (be careful — this is unencrypted)
4. In Vaultwarden: Tools > Import Data > Format: "LastPass (csv)"
5. Import the CSV
6. SECURELY DELETE the CSV file immediately:
shred -vfz -n 5 lastpass_export.csv
From 1Password
1. In 1Password desktop app: File > Export > All Vaults
2. Choose format: CSV or 1PUX
3. In Vaultwarden: Tools > Import Data > Format: "1Password (1pux)" or "1Password (csv)"
4. Import
5. Securely delete the export file
From KeePass/KeePassXC
1. In KeePassXC: Database > Export > CSV
2. In Vaultwarden: Tools > Import Data > Format: "KeePassX (csv)"
3. Import
4. Securely delete the export file
Maintenance and Updates
Updating Vaultwarden
cd /opt/vaultwarden
# Pull the latest image
docker compose pull
# Backup before updating (always!)
./backup-vaultwarden.sh
# Restart with the new image
docker compose up -d
# Check logs for errors
docker compose logs -f vaultwarden
# Verify the version
curl -s https://vault.homelab.example.com/api/config | jq .version
Monitoring
# Simple health check script
#!/bin/bash
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \
https://vault.homelab.example.com/api/alive)
if [ "$RESPONSE" != "200" ]; then
echo "ALERT: Vaultwarden is down! HTTP $RESPONSE"
# Send notification (email, webhook, etc.)
fi
Database Maintenance (SQLite)
# Check database integrity
sqlite3 /opt/vaultwarden/vw-data/db.sqlite3 "PRAGMA integrity_check;"
# Compact the database (reclaim space from deletions)
sqlite3 /opt/vaultwarden/vw-data/db.sqlite3 "VACUUM;"
# Check database size
ls -lh /opt/vaultwarden/vw-data/db.sqlite3
Best Practices Checklist
Here's a checklist for a production-grade self-hosted password manager:
| Item | Status | Notes |
|---|---|---|
| HTTPS enabled | Required | Never run without TLS |
| Strong master password | Required | 4+ random words or 16+ chars |
| 2FA on vault | Required | TOTP at minimum, hardware key preferred |
| Signups disabled | Required | After creating all accounts |
| Admin panel secured | Required | IP-restricted or disabled |
| Automated backups | Required | Daily, tested monthly |
| Offsite backups | Required | Encrypted, separate location |
| Fail2ban configured | Recommended | Brute force protection |
| Rate limiting enabled | Recommended | Built-in to Vaultwarden |
| Emergency access configured | Recommended | For incapacitation scenarios |
| Browser extension installed | Recommended | On all daily-use browsers |
| Mobile app configured | Recommended | For on-the-go access |
| Monitoring/alerting | Recommended | Know immediately if it goes down |
| Regular update schedule | Recommended | Monthly security updates |
| Export/backup tested | Required | Can you actually restore? |
Final Thoughts
Self-hosting your password manager is one of the most impactful things you can do in your homelab. It gives you complete control over your most sensitive data and removes dependency on cloud providers. Vaultwarden is the right choice for most homelabs — it's lightweight, feature-complete, and compatible with Bitwarden's excellent client ecosystem.
But please, take the backup strategy seriously. Your password vault is the one thing you absolutely cannot afford to lose. Automate your backups, encrypt them, store them offsite, and test your restores. Do this before you migrate your passwords, not after.
Once it's running and backed up, you'll wonder why you ever trusted someone else with your passwords.