Skip to Content

Vaultwarden Bitwarden Self-Host: Run Your Own Password Manager and Stop Trusting Anyone Else With Your Secrets

Learn how to deploy Vaultwarden — the lightweight Bitwarden-compatible server — with Docker, configure HTTPS, set up admin access, and connect the official Bitwarden apps to your own self-hosted vault.
Vaultwarden setup guide

Vaultwarden Bitwarden Self-Host: Run Your Own Password Manager and Stop Trusting Anyone Else With Your Secrets

A password manager is only as trustworthy as the company running it — and that company isn't you. Vaultwarden is an unofficial, lightweight, open-source implementation of the Bitwarden server API written in Rust. It runs on a $5 VPS, uses a fraction of the resources of the official Bitwarden server, and works with every official Bitwarden client: browser extensions, mobile apps, desktop apps, and the CLI. Your vault, your server, your rules. This guide walks you through a complete Vaultwarden Bitwarden self-host deployment from scratch.


Prerequisites

  • A Linux server (Ubuntu 22.04 LTS recommended) with at least 256MB RAM — Vaultwarden is impressively lightweight
  • Docker Engine and Docker Compose v2 installed
  • A domain name with DNS access — HTTPS is non-negotiable for a password manager; the Bitwarden clients refuse to connect over plain HTTP
  • Ports 80 and 443 open on your firewall
  • An email account for SMTP (for user invitations, 2FA recovery, and emergency access)

Verify your environment before starting:

docker --version
docker compose version
free -h

# Confirm ports 80 and 443 are free
sudo ss -tlnp | grep -E ':80|:443'

# Verify your domain resolves to this server
dig +short vault.yourdomain.com
# Must return your server IP before SSL provisioning

What Is Vaultwarden and How Does It Differ from Official Bitwarden?

Vaultwarden is a community-maintained Bitwarden server reimplementation. It speaks the same API as the official Bitwarden server, which means every official Bitwarden client — browser extensions, iOS, Android, desktop apps, and the CLI — works with it without any modification beyond pointing the server URL at your instance.

Vaultwarden vs. Official Bitwarden Self-Hosted

  • Resource requirements — the official Bitwarden self-hosted stack needs 2GB+ RAM and runs a dozen containers. Vaultwarden runs in a single container on 256MB RAM.
  • Features included free — Vaultwarden includes organizations, collections, groups, TOTP, emergency access, and the admin panel without any license. The official server locks most of these behind a paid license.
  • Database — Vaultwarden uses SQLite by default (or PostgreSQL/MySQL for larger deployments). The official server uses MSSQL.
  • Official support — Vaultwarden is community-supported. If you need SLAs and official Bitwarden backing, use the official server. For personal and team use, Vaultwarden is rock solid.

What You Get

  • Full vault sync across all Bitwarden clients
  • Organizations and collections for team password sharing
  • TOTP authenticator codes stored in vault
  • Secure notes, identities, and card storage
  • Emergency access
  • Bitwarden Send (encrypted file and text sharing)
  • Browser extension autofill on all major browsers
  • Bitwarden CLI for scripting and CI/CD secret access

Deploying Vaultwarden with Docker Compose

Project Setup

mkdir -p ~/vaultwarden
cd ~/vaultwarden

Docker Compose Configuration

# docker-compose.yml
version: '3.8'

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    ports:
      - "8080:80"
    environment:
      # Admin token — protect the /admin panel
      # Generate with: openssl rand -base64 48
      - ADMIN_TOKEN=${ADMIN_TOKEN}

      # Public domain — required for correct URL generation
      - DOMAIN=https://vault.yourdomain.com

      # Signup control
      - SIGNUPS_ALLOWED=true       # Set to false after team has registered
      - INVITATIONS_ALLOWED=true

      # Email / SMTP
      - SMTP_HOST=${SMTP_HOST}
      - SMTP_PORT=${SMTP_PORT}
      - SMTP_SECURITY=starttls     # or 'force_tls' for port 465
      - SMTP_USERNAME=${SMTP_USERNAME}
      - SMTP_PASSWORD=${SMTP_PASSWORD}
      - SMTP_FROM=${SMTP_FROM}
      - SMTP_FROM_NAME=Vaultwarden

      # WebSocket for live sync across clients
      - WEBSOCKET_ENABLED=true

      # Logging
      - LOG_LEVEL=warn
      - EXTENDED_LOGGING=true

    volumes:
      - vaultwarden_data:/data

volumes:
  vaultwarden_data:

Create the .env file with your credentials:

# .env

# Generate a strong admin token:
# openssl rand -base64 48
ADMIN_TOKEN=your-very-long-random-admin-token-here

# SMTP configuration
SMTP_HOST=smtp.yourdomain.com
SMTP_PORT=587
[email protected]
SMTP_PASSWORD=your-smtp-password
[email protected]

Start Vaultwarden:

docker compose up -d
docker compose logs -f vaultwarden

Watch for Rocket has launched from http://0.0.0.0:80 in the logs. Vaultwarden is running — but don't try to access it until HTTPS is configured. The Bitwarden clients will refuse to connect over HTTP.


Configuring HTTPS with Nginx

Vaultwarden requires HTTPS. This is not optional — it's a deliberate security requirement for a password manager. Set up Nginx as a TLS-terminating reverse proxy:

# /etc/nginx/sites-available/vaultwarden
server {
    listen 80;
    server_name vault.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name vault.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/vault.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/vault.yourdomain.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

    # Security headers appropriate for a password manager
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "no-referrer" always;

    # Vault web interface and API
    location / {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        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;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 90s;
    }

    # WebSocket endpoint for real-time vault sync
    location /notifications/hub {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /notifications/hub/negotiate {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
sudo ln -s /etc/nginx/sites-available/vaultwarden /etc/nginx/sites-enabled/
sudo nginx -t

# Get Let's Encrypt certificate
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d vault.yourdomain.com

sudo systemctl reload nginx

# Verify HTTPS is working
curl -I https://vault.yourdomain.com
# Should return HTTP/2 200 with security headers

Now open https://vault.yourdomain.com in your browser. You'll see the Vaultwarden web vault interface. Create your first account — the first account you create is a regular user, not an admin. Admin access is via the separate /admin panel using your ADMIN_TOKEN.


Admin Panel Configuration

Accessing the Admin Panel

The Vaultwarden admin panel lives at https://vault.yourdomain.com/admin. Enter your ADMIN_TOKEN to log in. This is separate from your vault account — it's a server administration interface, not a password manager account.

Key Settings to Configure

Work through these settings in the admin panel after first login:

  • General Settings → Domain URL — confirm it shows https://vault.yourdomain.com
  • Users → Allow signups — disable this after your team has registered so random people can't create accounts
  • SMTP → Test SMTP — send a test email to confirm email delivery works before you depend on it
  • 2FA → Disable 2FA remember — force users to enter 2FA on every new device rather than remembering trusted devices
  • Organizations → Allow org creation — control whether regular users can create organizations or only admins

Inviting Team Members

Once you've disabled open signups, invite team members from the admin panel (Users → Invite User) or from within a Bitwarden Organization. Invitations are sent via email with a link to create an account on your instance:

# Invite a user via the Vaultwarden API (useful for automation)
curl -X POST https://vault.yourdomain.com/api/organizations/ORG_ID/users/invite \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer USER_ACCESS_TOKEN' \
  -d '{
    "emails": ["[email protected]"],
    "type": 2,
    "collections": [],
    "accessAll": false
  }'

# Or create a user directly as admin (bypasses email invitation):
curl -X POST https://vault.yourdomain.com/admin/users/invite \
  -H 'Content-Type: application/json' \
  -b "admin_token=YOUR_ADMIN_TOKEN" \
  -d '{"email": "[email protected]"}'

Connecting Bitwarden Clients to Your Self-Hosted Instance

Browser Extensions

Install the official Bitwarden browser extension (Chrome, Firefox, Safari, Edge). On the login screen, click the region/server dropdown and select Self-hosted. Enter your server URL:

  • Server URL: https://vault.yourdomain.com

Leave all other fields blank — Vaultwarden serves all APIs from the same URL. Log in with your vault account credentials. Autofill and sync work identically to the cloud version.

Mobile Apps (iOS and Android)

In the official Bitwarden app, tap the region selector on the login screen → Self-hosted → enter https://vault.yourdomain.com. The app connects, syncs your vault, and behaves identically to the cloud version.

Bitwarden CLI

The CLI is useful for accessing secrets in scripts, CI/CD pipelines, and automation. Configure it to use your self-hosted instance:

# Install Bitwarden CLI
npm install -g @bitwarden/cli

# Point CLI at your self-hosted instance
bw config server https://vault.yourdomain.com

# Log in
bw login [email protected]

# Unlock the vault (returns a session key)
export BW_SESSION=$(bw unlock --raw)

# List items
bw list items | jq '[.[] | {name: .name, username: .login.username}]'

# Get a specific item's password
bw get password "My App Database"

# Use in CI/CD — get a secret by item name:
DB_PASSWORD=$(bw get password "production-db" --session $BW_SESSION)

# Sync before reading (to get latest changes)
bw sync

Tips, Gotchas, and Troubleshooting

Client Shows "Invalid Server URL" or "Cannot Connect"

Always the HTTPS URL — Bitwarden clients enforce HTTPS. Double-check:

# Test the API endpoint from your machine:
curl https://vault.yourdomain.com/api/config | jq .
# Should return Vaultwarden server version info

# Test the identity endpoint:
curl https://vault.yourdomain.com/identity/.well-known/openid-configuration
# Should return a JSON response

# If curl works but the client doesn't:
# 1. Check the URL in the client has https:// prefix
# 2. Check no trailing slash: https://vault.yourdomain.com NOT https://vault.yourdomain.com/
# 3. Clear the client's cache and try again

Vault Not Syncing in Real Time Across Devices

Real-time sync uses WebSockets via the /notifications/hub endpoint. If changes on one device don't appear on another until manual sync, the WebSocket proxy config is missing or incorrect. Verify the Nginx location blocks for /notifications/hub are in place and reload Nginx:

# Test WebSocket endpoint is reachable
curl -I https://vault.yourdomain.com/notifications/hub/negotiate
# Should return 200 or 404 (not 502 or connection refused)

# Check WEBSOCKET_ENABLED is true in your .env
docker exec vaultwarden env | grep WEBSOCKET

# Reload Nginx after any config changes
sudo nginx -t && sudo systemctl reload nginx

Emails Not Sending (Invitations, 2FA Recovery)

# Check Vaultwarden logs for SMTP errors
docker logs vaultwarden --tail 50 | grep -i smtp
docker logs vaultwarden --tail 50 | grep -i email
docker logs vaultwarden --tail 50 | grep -i error

# Test SMTP from the admin panel:
# https://vault.yourdomain.com/admin → SMTP Email Settings → Send test email

# Common SMTP issues:
# - Port 25 blocked on most VPS providers — use 587 (STARTTLS) or 465 (TLS)
# - Gmail/Google Workspace requires an App Password if 2FA is enabled
# - SMTP_SECURITY must match the port: 'starttls' for 587, 'force_tls' for 465

Forgot Admin Token

The admin token is in your .env file and the running container's environment. If you've lost it:

# View the current admin token from the running container
docker exec vaultwarden env | grep ADMIN_TOKEN

# Generate a new one and update .env:
openssl rand -base64 48

# Update ADMIN_TOKEN in .env and restart:
docker compose up -d --force-recreate vaultwarden

Updating Vaultwarden

cd ~/vaultwarden

# Pull the latest image
docker compose pull

# Restart — no manual migration needed
docker compose up -d

# Verify the new version is running
docker logs vaultwarden --tail 10 | grep -i version

# Pin to a specific version for production stability:
# image: vaultwarden/server:1.31.0

# Your vault data is in the Docker volume — it survives updates
docker volume inspect vaultwarden_vaultwarden_data

Backing Up Your Vault Data

All vault data — users, passwords, organizations — lives in a single SQLite file in the Docker volume. Back it up daily:

# Add to crontab: crontab -e
# Daily backup at 2am, retain 30 days
0 2 * * * docker exec vaultwarden sqlite3 /data/db.sqlite3 ".backup '/data/db.backup'" && \
  docker cp vaultwarden:/data/db.backup /opt/backups/vaultwarden-$(date +\%Y-\%m-\%d).sqlite && \
  find /opt/backups -name 'vaultwarden-*.sqlite' -mtime +30 -delete

# Also back up the entire data directory (includes attachments and config):
# docker run --rm -v vaultwarden_vaultwarden_data:/data \
#   -v /opt/backups:/backup alpine \
#   tar czf /backup/vaultwarden-full-$(date +%Y-%m-%d).tar.gz /data

Pro Tips

  • Enforce 2FA for all users — go to Admin Panel → General Settings → Require 2FA. Every account in your instance must have 2FA enabled to log in. Non-negotiable for a password manager holding your team's credentials.
  • Use Organizations for team password sharing — create a Bitwarden Organization in the web vault, invite team members, and use Collections to organize shared credentials by project or access level. Never share passwords by copy-pasting.
  • Restrict admin panel access by IP — add an Nginx allow/deny block to the /admin location so only your IP or your VPN subnet can reach it. The admin panel with a compromised token could expose everything.
  • Keep Vaultwarden on a dedicated server — avoid running a password manager on the same server as public-facing apps. Isolation limits blast radius if anything else on the server is compromised.
  • Use the Bitwarden CLI in CI/CD instead of hardcoded secrets — inject secrets at deploy time from your vault rather than storing them in environment files or CI/CD platform secrets. It's more auditable and easier to rotate.

Wrapping Up

A complete Vaultwarden Bitwarden self-host setup gives you and your team a production-grade password manager where every credential lives on a server you control — encrypted at rest, accessible only to authorized users, synced in real time across every device via the official Bitwarden apps. The setup takes under an hour, the ongoing maintenance is minimal, and the peace of mind of not trusting a third party with your most sensitive data is worth every minute of that hour.

Deploy with Docker Compose, configure HTTPS with Nginx, disable open registration after your team registers, enforce 2FA on every account, and set up daily backups. That's your complete security posture for a self-hosted password manager that your team can actually rely on.


Need a Secure Self-Hosted Stack Built for Your Team?

Deploying Vaultwarden is one piece of a larger self-hosted security posture. If you're moving your team's credentials, code, and internal tools onto infrastructure you control — with proper access controls, backups, and hardening — the sysbrix team can design and deploy the full stack. We get it done securely, not just quickly.

Talk to Us →
Self-Host Gitea: Run Your Own Git Server and Own Every Line of Code You Ship
Learn how to deploy Gitea with Docker, configure repositories and SSH access, set up CI/CD with Gitea Actions, and run a fully self-hosted Git platform your team can rely on.