Skip to main content

Open-source alternatives guide

Vaultwarden Advanced Setup 2026

Advanced Vaultwarden configuration for 2026 — security hardening, fail2ban, audit logs, organization management, PostgreSQL backend, monitoring, and now.

·OSSAlt Team
Share:

TL;DR

Vaultwarden (AGPL 3.0, ~40K GitHub stars) goes beyond a basic password manager when properly configured — hardened with fail2ban, rate limiting, Duo 2FA enforcement, and PostgreSQL. This guide covers the production setup: switching from SQLite to PostgreSQL for reliability, enforcing 2FA organization-wide, setting up audit logs, configuring fail2ban, and running a family or team vault with proper access controls. See the basic setup guide first.

Key Takeaways

  • Production backend: Switch from SQLite to PostgreSQL for better performance and reliability
  • Fail2ban: Block IPs after failed login attempts
  • 2FA enforcement: Require all users to have 2FA enabled
  • Audit logs: Track what happened to your vault
  • Organization hardening: Manage teams with collections and role-based access
  • Monitoring: Healthcheck endpoints and Prometheus metrics

Part 1: PostgreSQL Backend

For multi-user or production use, switch from SQLite to PostgreSQL:

# docker-compose.yml
services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - vaultwarden_data:/data
    environment:
      DOMAIN: "https://vault.yourdomain.com"
      DATABASE_URL: "postgresql://vaultwarden:${DB_PASSWORD}@db:5432/vaultwarden"
      SIGNUPS_ALLOWED: "false"
      ADMIN_TOKEN: "${ADMIN_TOKEN}"
      # Rate limiting:
      LOGIN_RATELIMIT_MAX_BURST: 10
      LOGIN_RATELIMIT_SECONDS: 60
      ADMIN_RATELIMIT_MAX_BURST: 5
      ADMIN_RATELIMIT_SECONDS: 300
      # 2FA enforcement:
      REQUIRE_DEVICE_EMAIL: "true"    # Require email 2FA if no other 2FA
      # Push notifications:
      PUSH_ENABLED: "true"
      PUSH_INSTALLATION_ID: "${PUSH_INSTALLATION_ID}"
      PUSH_INSTALLATION_KEY: "${PUSH_INSTALLATION_KEY}"
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: vaultwarden
      POSTGRES_USER: vaultwarden
      POSTGRES_PASSWORD: "${DB_PASSWORD}"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U vaultwarden"]
      interval: 10s
      start_period: 20s

volumes:
  vaultwarden_data:
  postgres_data:

Migrate from SQLite to PostgreSQL

# 1. Backup SQLite data first:
docker exec vaultwarden sqlite3 /data/db.sqlite3 ".backup /data/db-backup.sqlite3"

# 2. Start new Postgres-backed Vaultwarden
# (data directory will be empty — all users need to re-enroll, OR use migration)

# 3. Use vaultwarden-postgres-migration tool:
# https://github.com/pgerber/vaultwarden-postgres-migration
docker run --rm \
  -e SQLITE_URI=/data/db.sqlite3 \
  -e POSTGRES_URI="postgresql://vaultwarden:password@db:5432/vaultwarden" \
  -v vaultwarden_data:/data \
  ghcr.io/pgerber/vaultwarden-postgres-migration

Part 2: Fail2ban Integration

Block IPs after repeated failed login attempts:

# Add to docker-compose.yml:
services:
  fail2ban:
    image: crazymax/fail2ban:latest
    container_name: fail2ban
    restart: unless-stopped
    network_mode: host
    cap_add:
      - NET_ADMIN
      - NET_RAW
    volumes:
      - /var/log:/var/log:ro
      - ./fail2ban:/data
# ./fail2ban/jail.d/vaultwarden.conf
[vaultwarden]
enabled = true
port = 80,443
filter = vaultwarden
action = iptables-allports[name=vaultwarden, chain=FORWARD]
logpath = /var/log/docker/vaultwarden.log
maxretry = 5
bantime = 3600
findtime = 600

[vaultwarden-admin]
enabled = true
port = 80,443
filter = vaultwarden-admin
action = iptables-allports[name=vaultwarden-admin, chain=FORWARD]
logpath = /var/log/docker/vaultwarden.log
maxretry = 3
bantime = 86400
findtime = 600
# ./fail2ban/filter.d/vaultwarden.conf
[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>.*$
            ^.*Failed login attempt for .* from <ADDR>.*$
ignoreregex =

# ./fail2ban/filter.d/vaultwarden-admin.conf
[Definition]
failregex = ^.*ADMIN-TOKEN.*Invalid admin token\. IP: <ADDR>.*$
ignoreregex =

Part 3: Enforce 2FA Organization-Wide

Require all users to enable 2FA:

environment:
  # Require 2FA — users without it are blocked until they enroll
  REQUIRE_DEVICE_EMAIL: "true"

Via the Admin Panel (/admin):

  1. Users → select user → Enable 2FA Required
  2. Or in Organization settings → Require 2FA for all members

Users without 2FA see a mandatory enrollment prompt on next login.

Supported 2FA methods:

  • TOTP (Google Authenticator, Aegis, Bitwarden Authenticator)
  • YubiKey OTP
  • FIDO2/WebAuthn (hardware security keys, passkeys)
  • Email OTP (fallback)
  • Duo

Part 4: Security Headers

Add security headers via Caddy:

vault.yourdomain.com {
    header {
        # Security headers:
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "SAMEORIGIN"
        X-XSS-Protection "1; mode=block"
        Referrer-Policy "strict-origin-when-cross-origin"
        Permissions-Policy "interest-cohort=()"
        # Content Security Policy:
        Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self' https://push.bitwarden.com https://notifications.bitwarden.com; frame-src 'self'"
    }
    reverse_proxy localhost:8080
}

Part 5: Email Verification and Invitations

When signups are disabled, manage users via invitations:

# Invite a user via admin panel:
# Admin → Users → Invite User → enter email

# Or via CLI:
docker exec vaultwarden curl -X POST http://localhost:80/api/organizations/ORG_ID/users/invite \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ADMIN_TOKEN" \
  -d '{"emails":["alice@yourdomain.com"],"accessAll":false,"type":2}'

Restrict invitations to specific email domains:

environment:
  SIGNUPS_DOMAINS_WHITELIST: "yourdomain.com,yourcompany.com"
  SIGNUPS_ALLOWED: "false"    # Still disable open signups
  SIGNUPS_VERIFY: "true"      # Require email verification

Part 6: Organizations and Collections Deep Dive

Organization Roles

RoleCan Do
OwnerAll settings, billing, delete org
AdminManage users, collections
ManagerManage assigned collections
MemberAccess assigned collections
CustomGranular permissions

Collections Strategy

Design your collections for access control:

Organization: "Company"
├── Collection: "Shared Infrastructure" (Admins + DevOps team)
│   ├── AWS Root Account
│   ├── Kubernetes certs
│   └── VPN credentials
├── Collection: "Engineering" (All engineers - Read)
│   ├── GitHub tokens
│   └── Dev environment credentials
├── Collection: "Finance" (Finance team only)
│   ├── Bank accounts
│   └── Payment processors
└── Collection: "HR" (HR only)
    ├── HRIS credentials
    └── Benefits portal

Per-collection permissions:

  • Can view: See and copy passwords (can't see full password)
  • Can edit: Full CRUD, edit metadata
  • Hide passwords: Can autofill but never see the actual password

Part 7: Admin Audit Logs

Enable audit logging to track vault events:

environment:
  ORG_EVENTS_ENABLED: "true"      # Enable org event log
  EVENTS_DAYS_RETAIN: 90          # Keep events for 90 days

Events tracked:

  • Login attempts (success/failure)
  • Item created/modified/deleted
  • Collection access granted/revoked
  • 2FA enrolled/removed
  • Admin actions

View in Admin Panel → Event Logs or Organization → Event Log.


Part 8: Backup Strategy

PostgreSQL Backup

#!/bin/bash
# backup-vaultwarden.sh

set -e

DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR="/backup/vaultwarden"
mkdir -p "$BACKUP_DIR"

# Database dump:
docker exec vaultwarden_db pg_dump -U vaultwarden vaultwarden \
  | gzip > "$BACKUP_DIR/db-${DATE}.sql.gz"

# Attachments and icons:
tar -czf "$BACKUP_DIR/data-${DATE}.tar.gz" \
  $(docker volume inspect vaultwarden_vaultwarden_data --format '{{.Mountpoint}}')

# Remove backups older than 30 days:
find "$BACKUP_DIR" -name "*.gz" -mtime +30 -delete

# Notify on success:
curl -s -d "Vaultwarden backup complete: db-${DATE}.sql.gz" \
  https://ntfy.yourdomain.com/backups

S3 Offsite Backup

# Add to backup script:
aws s3 sync "$BACKUP_DIR" "s3://your-bucket/vaultwarden/" \
  --exclude "*" --include "*.gz" \
  --delete

Part 9: Monitoring

Health Check

# Vaultwarden health endpoint:
curl https://vault.yourdomain.com/alive
# Returns: {"version":"...", "status":"ok"}

Uptime Kuma Monitor

  1. Add HTTP(S) monitor: https://vault.yourdomain.com/alive
  2. Alert via Slack/ntfy if it goes down

Prometheus Metrics

environment:
  # Metrics available at /metrics (enable in admin panel)
  METRICS_ENABLED: "true"
# prometheus.yml:
scrape_configs:
  - job_name: "vaultwarden"
    metrics_path: /metrics
    static_configs:
      - targets: ["vault.yourdomain.com:80"]
    basic_auth:
      username: metrics
      password: your-metrics-password

Part 10: Emergency Access Configuration

Set up emergency access before you need it:

  1. Settings → Emergency Access → Add Emergency Contact
  2. Enter their email (they need a Bitwarden account on your Vaultwarden)
  3. Type:
    • View: Can view all items in your vault
    • Takeover: Can reset your master password (full account access)
  4. Wait time: 5-7 days recommended
  5. They receive an email confirming their role

When they invoke emergency access:

  1. They click "Request access" in their Bitwarden app
  2. You receive an email and have the wait period to deny it
  3. After the wait period: access is automatically granted
  4. You can approve early or deny any time during the wait

Maintenance

# Update Vaultwarden:
docker compose pull
docker compose up -d

# Run migrations (usually auto-runs on startup):
docker compose logs vaultwarden | grep -i migrat

# Check connected clients:
# Admin → Users → [User] → Sessions

# Logs with timestamps:
docker compose logs -f --timestamps vaultwarden

# Admin panel: https://vault.yourdomain.com/admin

Network Architecture and Exposure Hardening

A password manager is your highest-value self-hosted target. It contains credentials for every other system you operate, making its security posture uniquely important. The configuration in the earlier sections covers application-level hardening; this section addresses the network and infrastructure layer.

The most important principle is minimizing the attack surface exposed to the public internet. Your Vaultwarden instance needs to be reachable by your clients — browsers, the Bitwarden apps on mobile and desktop — but the admin panel, the Prometheus metrics endpoint, and any management interfaces should never be publicly accessible. The Caddy or Nginx configuration should only proxy the following paths publicly: the main application root, the API endpoint, the web vault, the icons endpoint, and the notifications websocket. Everything else, including the admin panel at /admin, should be restricted to your VPN subnet or internal IP range via your reverse proxy configuration.

Cloudflare in proxy mode adds a useful layer of protection: your server's real IP address is hidden behind Cloudflare's network, DDoS protection is free, and Cloudflare's WAF can block common attack patterns before they reach your instance. The tradeoff is that traffic between Cloudflare's edge and your server is decrypted and re-encrypted at Cloudflare's infrastructure. For a password manager, some organizations prefer a direct TLS connection without an intermediary that terminates the connection. Both approaches are defensible — the decision depends on your threat model and how you weigh DDoS resilience against the Cloudflare intermediary. If you run Vaultwarden exclusively for yourself or a small team behind a VPN, direct TLS with no public exposure is the cleanest option.

Fail2ban, as configured in Part 2, handles automated blocking of login attempts. For a more proactive approach, consider IP allowlisting: if your Vaultwarden instance is only ever accessed from known IP ranges (your home IP, your office IP, VPN exit nodes), you can configure your firewall to block all other source IPs at the infrastructure level before any traffic reaches the application. This is particularly effective for small teams and family deployments where the user population is small and the expected IP ranges are stable.

The push notification service is an optional feature that enables real-time sync across Bitwarden clients — when you update a password on your phone, your browser extension updates immediately rather than waiting for the next polling interval. Enabling this feature requires registering with Bitwarden's push notification relay (which is a public Bitwarden service, even for Vaultwarden users). If you are operating a high-security installation and want zero dependencies on external services, disable push notifications and accept that sync will be polling-based rather than real-time. For most deployments, the convenience of push notifications outweighs the minimal data exposure (only the fact that a sync event occurred, not credentials themselves, passes through the relay).

Cost Comparison: Vaultwarden vs 1Password vs Bitwarden Teams

The financial argument for self-hosting Vaultwarden is among the strongest in the self-hosted ecosystem because commercial password manager pricing is both high and scales per user.

1Password Teams costs $19.95 per user per month for the Teams plan. A team of 10 pays $199.50 per month or $2,394 per year. A team of 50 pays $997.50 per month or $11,970 per year. 1Password Business at $7.99 per user per month (billed annually) is somewhat cheaper but still totals $4,794 per year for a 50-person team. These costs are for unlimited vaults, user management, and integrations — capabilities that Vaultwarden provides completely free on self-hosted infrastructure.

Bitwarden Teams — the officially hosted version of the same codebase Vaultwarden implements — costs $4 per user per month, making it significantly cheaper than 1Password and a more honest comparison. A 50-person team pays $2,400 per year. Self-hosted Vaultwarden on a Hetzner CX11 ($4/month) with Backblaze B2 backups ($1/month) costs $60 per year — a 97% reduction compared to Bitwarden Teams and a 98% reduction compared to 1Password Business. Even accounting for the time investment to set up and maintain the instance, the ROI for any team larger than five is compelling.

The best open source alternatives to 1Password covers the full landscape of credential management alternatives including KeePassXC, Passbolt, and others, if Vaultwarden's Bitwarden-compatible approach is not the right fit for your team's requirements. For most self-hosters, however, Vaultwarden's combination of full Bitwarden app compatibility, active development, mature security model, and 40,000+ GitHub star community makes it the default recommendation in this category.

Ecosystem Integration: Connecting Vaultwarden to Your Stack

Vaultwarden integrates with the broader self-hosted ecosystem in several practical ways that extend its value beyond basic credential storage.

The Bitwarden Secrets Manager, available as a beta feature in newer Vaultwarden releases, allows storing and accessing API keys and environment variables programmatically. This is the bridge between your human-facing credential vault and your infrastructure-as-code workflows. CI/CD pipelines, Docker deployments, and automation scripts can retrieve secrets at runtime rather than storing them in environment files on disk. For teams running a self-hosted application stack — deploying services with Coolify or Dokploy — using Vaultwarden as a secrets backend reduces the risk of accidentally committing credentials to Git repositories.

The Vaultwarden admin API enables programmatic management of users, organizations, and collections. Combined with your identity provider — whether Authentik, Logto, or Keycloak — you can automate the full lifecycle of credential access: create a Vaultwarden account when a new employee is added to the identity provider, add them to the appropriate collections based on their team membership, and revoke access when they offboard. This automation closes the gap that many self-hosted credential management setups leave open: the manual work of keeping vault membership in sync with directory membership.

For monitoring, Vaultwarden's Prometheus metrics endpoint (enabled in the admin panel) provides insight into active sessions, login attempts, failed authentications, and request rates. Adding Vaultwarden to your Grafana and Prometheus observability stack means login anomalies surface in your alerting alongside other infrastructure events. A spike in failed authentication attempts triggers a Prometheus alert before fail2ban has to resort to IP bans — giving you visibility into brute force attempts at the metrics layer, not just the log layer.

Migration from 1Password or Bitwarden Cloud

Migrating to self-hosted Vaultwarden from a commercial password manager is one of the most friction-free migrations in the self-hosted space, primarily because Vaultwarden fully implements the Bitwarden API and all Bitwarden clients can connect to it.

If you are migrating from Bitwarden Cloud, the process is straightforward: export your vault from Bitwarden's web vault (Settings → Export Vault), spin up your Vaultwarden instance, and import the export file. All your credentials, folders, and secure notes migrate intact. After import, update your Bitwarden app settings to point to your self-hosted server URL instead of the default Bitwarden cloud endpoint. The app settings change takes about thirty seconds per device. Your organization members need to accept a new organization invite on the self-hosted instance, but their individual vault items migrate automatically if they export and re-import.

Migrating from 1Password to Vaultwarden requires an intermediate step. Export your 1Password vault as a 1Password Interchange Format (1PIF) file or CSV, then use the Bitwarden importer (which understands the 1Password export format) to create a Bitwarden-compatible export, then import into Vaultwarden. The 1Password importer handles most vault item types — logins, secure notes, credit cards, identities, and documents — with reasonably high fidelity. Some 1Password-specific features like Travel Mode and watchtower security reports have no direct equivalents in Vaultwarden, but the core credential storage and autofill functionality migrates cleanly. The best open source alternatives to 1Password article covers the full comparison in more detail, including how Passbolt handles team credential sharing differently than Vaultwarden's organization model.

LastPass to Vaultwarden migration is similarly well-supported. Bitwarden's importer handles LastPass CSV exports natively. Given LastPass's high-profile security breaches in 2022-2023, many organizations accelerated their evaluation of self-hosted alternatives. Vaultwarden is the most common destination for LastPass migrations in the self-hosting community because the Bitwarden client ecosystem is mature, familiar, and available on every platform.

The key migration consideration for any source platform is secure notes with file attachments. File attachments stored in your previous password manager may need to be re-uploaded manually in Vaultwarden, as most export formats do not include attachment binary data — only references or names. Plan your migration window with time to verify that critical attachments transferred correctly before canceling your previous subscription.


See all open source security and privacy tools at OSSAlt.com/categories/security.

See open source alternatives to Vaultwarden on OSSAlt.

The SaaS-to-Self-Hosted Migration Guide (Free PDF)

Step-by-step: infrastructure setup, data migration, backups, and security for 15+ common SaaS replacements. Used by 300+ developers.

Join 300+ self-hosters. Unsubscribe in one click.