← All articles
SECURITY Self-Hosted Password Management: Vaultwarden and Pas... 2026-02-09 · 12 min read · passwords · vaultwarden · bitwarden

Self-Hosted Password Management: Vaultwarden and Passbolt

Security 2026-02-09 · 12 min read passwords vaultwarden bitwarden passbolt security

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.

Vaultwarden logo

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:

Disadvantages:

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

  1. Start Vaultwarden and navigate to https://vault.homelab.example.com
  2. Create your account (this is your master account)
  3. Immediately disable signups by setting SIGNUPS_ALLOWED=false and restarting
  4. Access the admin panel at /admin to configure additional settings
  5. 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

  1. Install the Bitwarden extension from your browser's extension store (Chrome, Firefox, Safari, Edge)
  2. Click the extension icon > Settings (gear)
  3. Under "Self-hosted environment", enter:
    • Server URL: https://vault.homelab.example.com
  4. Save and log in with your credentials

Mobile Apps

  1. Install Bitwarden from the App Store (iOS) or Play Store (Android)
  2. On the login screen, tap the region selector (or settings gear)
  3. Choose "Self-hosted" and enter:
    • Server URL: https://vault.homelab.example.com
  4. Log in

Important: Your Vaultwarden instance must be accessible from the internet for mobile access outside your home. Options:

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

  1. Edit a vault entry
  2. Click "TOTP" field
  3. Enter the TOTP secret key (or scan the QR code with the mobile app)
  4. 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:

  1. Log into the web vault
  2. Go to Settings > Security > Two-step Login
  3. Enable "FIDO2 WebAuthn" and register your key
  4. 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

  1. In the web vault, go to Settings > Emergency Access
  2. Click "Invite emergency contact"
  3. Enter their email and choose access level:
    • View: They can see (but not edit) your vault
    • Takeover: They get full access to your vault
  4. 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:

  1. Log into vault.bitwarden.com
  2. Go to Tools > Export Vault
  3. Choose format: Encrypted JSON (recommended) or JSON
  4. Export and save the file
  5. Log into your Vaultwarden instance
  6. Go to Tools > Import Data
  7. 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.