Vaultwarden Password Manager Self-Host Guide via Docker

Vaultwarden Password Manager Self-Host Guide via Docker

Bitnesia Security Apr 16, 2026 529 ID

Vaultwarden is an unofficial Bitwarden server implementation rewritten in Rust by the open-source community. This project is fully compatible with all official Bitwarden clients, such as browser extensions, desktop applications, Android, and iOS, so the end-user experience is identical to the official Bitwarden.

Architecturally, Vaultwarden runs as a single lightweight binary that includes the web vault interface, REST API, and WebSocket server in one Docker container. The default database used is SQLite. This makes it extremely easy to deploy and backup without external dependencies.

Why Choose Vaultwarden?

There are several strong reasons to run Vaultwarden on your own server instead of relying on third-party cloud services:

  1. Free premium features: Vaultwarden enables all Bitwarden Premium features for free, including TOTP Authenticator, Bitwarden Send, Emergency Access, File Attachments, and Organizations/Sharing, without any subscription fees.
  2. Resource efficient: Vaultwarden's RAM consumption is only around 10–30 MB when idle. This is far more efficient than the official Bitwarden Server, which requires a .NET + MSSQL stack with a minimum RAM requirement of 2 GB.
  3. Full privacy: All encrypted data is stored on your own server. No data is sent to Bitwarden, Inc. or any third party.
  4. Full control: You determine registration policies, data retention, and backup frequency according to your needs.
Vaultwarden vs Bitwarden Official Differences
AspectVaultwardenBitwarden Official (Self-Hosted)
Language / RuntimeRust (single binary).NET + ASP.NET Core
Default databaseSQLite (optional PostgreSQL/MySQL)Microsoft SQL Server
Minimum RAM requirement~256 MB (including OS)~2 GB (full stack)
Premium featuresFree, all enabledRequires paid license
Official statusUnofficial, communityOfficial from Bitwarden, Inc.
Deployment complexitySingle Docker containerMulti-container (10+ services)
Security auditAvailable (see official wiki)Available (internal + external)

Preparation and System Requirements

Hardware Requirements

Vaultwarden is very lightweight and runs comfortably on various hardware. Below are minimum recommendations based on usage scale:

ScenarioHardwareExample devices
Personal / family (<10 users)1 vCPU, 512 MB RAM, 5 GB storageEntry-level VPS, Raspberry Pi 2+, Synology NAS
Small team (10–50 users)1 vCPU, 1 GB RAM, 20 GB storageStandard cloud VPS
Organization (50+ users)2 vCPU, 2 GB RAM, 40 GB storageMedium cloud VPS, bare metal

Software Requirements

Ensure your operating system (Ubuntu 24.04 LTS or Debian 13 recommended) has the following packages installed:

  1. Docker Engine version 24.0 or newer.
  2. Docker Compose plugin version 2.x (integrated in modern Docker Engine).
  3. curl and openssl for configuration purposes.

Install Docker Engine

Run the following command to install Docker Engine:

sudo sh -c "curl -fsSL https://get.docker.com/ | sh"

Domain and SSL Requirements

HTTPS is a mandatory requirement to run Vaultwarden functionally. Bitwarden clients (browser extensions, mobile apps) require an HTTPS connection to access the server. Features such as synchronization, push notifications, and two-factor authentication will not work without SSL.

There are two main approaches to obtaining HTTPS:

  1. Domain + Reverse Proxy + Let's Encrypt: This method is most recommended. Use Nginx Proxy Manager or Traefik to manage automatic SSL certificates via Let's Encrypt.
  2. Cloudflare Tunnel: This method is an alternative that does not require opening ports 80/443 on the server firewall. This is suitable for networks behind NAT or without a public IP.

Ensure you already have an active domain name (e.g., vault.example.com) and access to your DNS panel before proceeding to the installation stage.

Vaultwarden Installation Steps

Step 1: Create docker-compose.yml File

Create a dedicated directory for Vaultwarden. Separating this directory is important to make the backup process easier and isolated from other Docker services.

sudo mkdir /opt/vaultwarden
cd /opt/vaultwarden

Configure docker-compose.yml

Create a docker-compose.yml file inside the /opt/vaultwarden/ directory.

sudo nano docker-compose.yml

Enter the configuration:

services:
  vaultwarden:
    image: vaultwarden/server:1.35.7
    container_name: vaultwarden
    restart: unless-stopped
    env_file:
      - .env
    volumes:
      # Mount data folder into container
      - ./vw-data:/data
    ports:
      # Bind to localhost only — no need to expose to public
      # Port 80 for HTTP (reverse proxy handles HTTPS)
      - "127.0.0.1:8080:80"
      # WebSocket port (required if WEBSOCKET_ENABLED: true)
      - "127.0.0.1:3012:3012"
    networks:
      - internal
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 20s

  npm:
    image: jc21/nginx-proxy-manager:2.14.0
    container_name: nginx-proxy-manager
    restart: unless-stopped
    ports:
      - "80:80"
      - "81:81"
      - "443:443"
    environment:
      DB_SQLITE_FILE: "/data/database.sqlite"
    volumes:
      - ./npm-data:/data
      - ./npm-letsencrypt:/etc/letsencrypt
    depends_on:
      - vaultwarden
    networks:
      - internal

networks:
  internal:

Configure .env for Vaultwarden

Before creating the .env file, generate an ADMIN_TOKEN using argon2. The latest Vaultwarden version recommends tokens in Argon2id hashed format for better security:

# Install argon2 if not already present
sudo apt install -y argon2

# Generate token: replace 'your_secret_password' with a strong password
echo -n "your_secret_password" \
  | argon2 "$(openssl rand -base64 32)" -e -id -k 65540 -t 3 -p 4

Copy the generated Argon2id hash output (format starts with $argon2id$v=...). This hash will be used as the value for ADMIN_TOKEN.

Create the .env file:

sudo nano .env

Enter the configuration:

# Public URL of Vaultwarden — must be filled with your domain
DOMAIN: "https://vault.example.com"

# Admin panel — fill with the Argon2id hash that was generated
# Access admin panel: https://vault.example.com/admin
ADMIN_TOKEN: '$argon2id$v=19$m=65540,t=3,p=4$...'

# Disable new account registration after creating the first account
SIGNUPS_ALLOWED: "false"

# Disable invitation feature (optional, for extra security)
INVITATIONS_ALLOWED: "false"

# Enable WebSocket for real-time notifications in browser/desktop
WEBSOCKET_ENABLED: "true"

# Server time zone
TZ: "Asia/Jakarta"

# Log level (use "warn" in production to reduce noise)
LOG_LEVEL: "warn"

Step 2: Run Docker Compose

Run docker compose:

sudo docker compose up -d

Check the status of containers in docker compose:

sudo docker compose ps

The output should look like this:

NAME                  IMAGE                             COMMAND       SERVICE       CREATED          STATUS                    PORTS
nginx-proxy-manager   jc21/nginx-proxy-manager:2.14.0   "/init"       npm           33 minutes ago   Up 33 minutes             0.0.0.0:80-81->80-81/tcp, [::]:80-81->80-81/tcp, 0.0.0.0:443->443/tcp, [::]:443->443/tcp
vaultwarden           vaultwarden/server:1.35.7         "/start.sh"   vaultwarden   33 minutes ago   Up 33 minutes (healthy)   127.0.0.1:3012->3012/tcp, 127.0.0.1:8080->80/tcp

Check the vaultwarden container logs:

sudo docker logs vaultwarden

The log output should look like this:

/--------------------------------------------------------------------\
|                        Starting Vaultwarden                        |
|                           Version 1.35.7                           |
|--------------------------------------------------------------------|
| This is an *unofficial* Bitwarden implementation, DO NOT use the   |
| official channels to report bugs/features, regardless of client.   |
| Send usage/configuration questions or feature requests to:         |
|   https://github.com/dani-garcia/vaultwarden/discussions or        |
|   https://vaultwarden.discourse.group/                             |
| Report suspected bugs/issues in the software itself at:            |
|   https://github.com/dani-garcia/vaultwarden/issues/new            |
\--------------------------------------------------------------------/

Check the nginx-proxy-manager container logs:

sudo docker logs nginx-proxy-manager

The log output should look like this:

-------------------------------------
 _   _ ____  __  __
| \ | |  _ \|  \/  |
|  \| | |_) | |\/| |
| |\  |  __/| |  | |
|_| \_|_|   |_|  |_|
-------------------------------------
User:  npm PUID:0 ID:0 GROUP:0
Group: npm PGID:0 ID:0
-------------------------------------

❯ Starting nginx ...
❯ Starting backend ...

Verify port binding at the host level:

ss -tulnp | grep -E '80|81|443|8080|3012'

The output should look like this:

tcp   LISTEN 0      4096         0.0.0.0:81        0.0.0.0:*          
tcp   LISTEN 0      4096         0.0.0.0:80        0.0.0.0:*          
tcp   LISTEN 0      4096         0.0.0.0:443       0.0.0.0:*          
tcp   LISTEN 0      4096       127.0.0.1:8080      0.0.0.0:*          
tcp   LISTEN 0      4096       127.0.0.1:3012      0.0.0.0:*          
tcp   LISTEN 0      4096            [::]:81           [::]:*          
tcp   LISTEN 0      4096            [::]:80           [::]:*          
tcp   LISTEN 0      4096            [::]:443          [::]:*

Step 3: Configure Proxy Host

Configure Proxy Host for Vaultwarden in NPM:

  1. Log in to the NPM dashboard at http://IP_SERVER:81.
  2. Click Proxy Hosts → Add Proxy Host.
  3. Enter Domain Names: vault.example.com.
  4. Enter Forward Hostname/IP: vaultwarden.
  5. Enter Forward Port: 80.
  6. Check Websockets Support (required for real-time sync).
  7. In the SSL tab, select Request a new SSL Certificate, then enable Force SSL and HTTP/2 Support.
  8. In the Settings tab, enter custom Nginx configuration for WebSocket:
location /notifications/hub {
  proxy_pass http://vaultwarden:3012;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
}

Open a browser and access https://vault.example.com. If the Vaultwarden login page appears, the installation was successful.

Initial Configuration and Security

Creating the First Account

Immediately after installation, create your administrator account before disabling public registration:

  1. Open https://vault.example.com in your browser.
  2. Click Create Account.
  3. Enter your email address, name, and a strong master password (minimum 12 characters, combination of uppercase/lowercase letters, numbers, and symbols).
  4. Click Submit. The account is activated immediately without email verification by default.

Warning: Master password cannot be recovered if forgotten. Store it in a safe offline location. Bitwarden or Vaultwarden uses end-to-end encryption. This means even the server administrator cannot access your vault if they do not know the master password.

Enabling and Securing the Admin Panel

The Vaultwarden admin panel can be accessed at https://vault.example.com/admin using the ADMIN_TOKEN that was configured earlier. You can manage users, view statistics, send invitations, and change server configuration in real-time through the admin panel.

Recommended admin panel security steps:

  • Use an Argon2id token (not plaintext) as configured in the previous step.
  • Restrict access by IP at the reverse proxy level. Add the following rule in the Nginx configuration to restrict /admin access only from specific IPs:
# Add inside the Nginx server block (before location /)
location /admin {
  # Allow only your admin IP
  allow 203.0.113.10;   # Replace with your public IP
  deny all;

  proxy_pass         http://127.0.0.1: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;
}
  • Completely disable the admin panel if not needed by removing or emptying the ADMIN_TOKEN variable in docker-compose.yml, then restart the container.

Disabling New Account Registration

After all required accounts have been created, disable public registration to prevent strangers from signing up on your server. Ensure the following values are present in the .env configuration:

# Disable account registration
SIGNUPS_ALLOWED: "false"
# Also disable invitation feature (optional, for extra security)
INVITATIONS_ALLOWED: "false"

Restart the container for changes to take effect after modifying the configuration:

sudo docker compose restart vaultwarden

Re-enable SIGNUPS_ALLOWED: "true" temporarily if you need to add new users later. Create the account, then disable it again or use the invitation feature through the admin panel.

Enabling Two-Factor Authentication (2FA)

2FA is highly recommended for every account on your Vaultwarden server. Vaultwarden supports various 2FA methods:

  • Authenticator App (TOTP): Google Authenticator, Aegis (Android), Raivo (iOS).
  • Email OTP: Requires SMTP configuration.
  • YubiKey OTP: Hardware security key.
  • FIDO2 / WebAuthn: Passkeys, hardware keys (YubiKey 5, SoloKey).
  • Duo: Duo Security service integration.

Enabling 2FA via Authenticator App (TOTP)

  1. Log in to your vault at https://vault.example.com.
  2. Click profile name → Account Settings.
  3. Select the Security → Two-step Login tab.
  4. Click Manage on the Authenticator App row.
  5. Scan the QR code with your authenticator app on your phone.
  6. Enter the 6-digit code for verification, then click Enable.
  7. Save recovery codes in a safe offline location.

SMTP Configuration

Configure SMTP for email notifications; optional but recommended. Email is required for email verification features, new login notifications, and account management. Add SMTP variables to .env:

# ... other variables ...
SMTP_HOST: "smtp.gmail.com"
SMTP_FROM: "[email protected]"
SMTP_FROM_NAME: "Vaultwarden"
SMTP_SECURITY: "starttls"   # Options: "starttls", "force_tls", "off"
SMTP_PORT: "587"
SMTP_USERNAME: "[email protected]"
SMTP_PASSWORD: "your-app-password"  # Use Google App Password
# Require email verification before account is active
REQUIRE_DEVICE_EMAIL: "true"

Install Fail2Ban

Install Fail2Ban to automatically block IPs that perform brute-force login attempts.

Install fail2ban:

sudo apt install -y fail2ban

Create a filter for Vaultwarden:

sudo tee /etc/fail2ban/filter.d/vaultwarden.conf <<'EOF'
[INCLUDES]
before = common.conf

[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
ignoreregex =
journalmatch = _SYSTEMD_UNIT=docker.service
EOF

# Create jail for Vaultwarden
sudo tee /etc/fail2ban/jail.d/vaultwarden.conf <<'EOF'
[vaultwarden]
enabled  = true
port     = 80,443,8080
filter   = vaultwarden
logpath  = /opt/vaultwarden/data/vaultwarden.log
maxretry = 5
bantime  = 3600
findtime = 600
EOF

Restart fail2ban:

sudo systemctl restart fail2ban
sudo fail2ban-client status vaultwarden

Migration and Daily Usage

Importing Data from Other Password Managers

Vaultwarden accepts the same import formats as Bitwarden. The import process is done through the web vault interface:

From Bitwarden Cloud

  1. Log in to https://vault.bitwarden.com.
  2. Click Tools → Export Vault.
  3. Select JSON (Encrypted) format for best security, or JSON for direct import.
  4. Log in to your Vaultwarden server, then click Tools → Import Data.
  5. Select Bitwarden (json) as the format, upload the file, then click Import Data.

From Google Chrome Password Manager

  1. Open Chrome → Settings → Passwords.
  2. Click the three dots icon on the right → Export Passwords → save as CSV.
  3. In your Vaultwarden vault: Tools → Import Data → select Chrome (csv) → upload file.

From LastPass

  1. Log in to LastPass → Advanced Options → Export.
  2. Save the downloaded CSV file.
  3. In Vaultwarden: Tools → Import Data → select LastPass (csv) → upload.

From KeePass / KeePassX

  1. Export the KeePass database to XML format.
  2. In Vaultwarden: Tools → Import Data → select KeePass 2 (xml) → upload.

Security during import: CSV files from Chrome or LastPass contain passwords in plaintext. Delete these files immediately after the import process is complete. Use shred -u filename.csv on Linux to securely delete.

Client Setup: Connecting to Your Own Server

All official Bitwarden clients support custom server URL configuration. Here's how to connect them to your Vaultwarden instance:

Browser Extensions (Chrome, Firefox, Edge, Safari)

  1. Install the Bitwarden extension from your browser's extension store.
  2. Open the extension, then click the gear icon (Settings) or click the server region on the login screen.
  3. Select Self-hosted.
  4. Enter Server URL: https://vault.example.com.
  5. Click Save, then log in with your account.

Mobile Apps (Android / iOS)

  1. Install the Bitwarden app from the Google Play Store or Apple App Store.
  2. On the login screen, tap Logging in on: or the Self-hosted icon.
  3. Enter Server URL: https://vault.example.com.
  4. Tap Save, then log in.

Desktop Apps (Windows, macOS, Linux)

  1. Download Bitwarden Desktop from https://bitwarden.com/download.
  2. On the login screen, click Accessing: → Self-hosted.
  3. Enter Server URL, click Save, then log in.

Organization Features and Password Sharing

Vaultwarden fully supports Bitwarden Organizations features. This is ideal for sharing passwords within a family or small team:

  • Creating an Organization: Log in to the web vault, click profile name → New Organization → enter name and billing email (you can use a dummy email for self-hosted).
  • Inviting Members: In the Organization dashboard, select the Members tab → Invite Member → enter member's email.
  • Creating Collections: Collections are shared folders within an Organization. Create collections based on categories, such as "Family", "Work", or "Server Credentials".
  • Sharing Items: Select the appropriate Organization and Collection when adding or editing vault items so that the item can be accessed by all members.

Maintenance and Backup

How to Update Vaultwarden

Vaultwarden regularly releases updates with security fixes and new features. The update process using Docker is very simple:

cd /opt/vaultwarden

# Pull the latest image from Docker Hub
sudo docker compose pull

# Recreate container with new image (downtime around 5–10 seconds)
sudo docker compose up -d --remove-orphans

# Verify container is running with the latest image
sudo docker compose ps
sudo docker inspect vaultwarden | grep Image

Check the Vaultwarden Releases page before updating to read the changelog and ensure there are no breaking changes that require migration preparation.

Database Backup Strategy

This is the most critical part of managing Vaultwarden. Losing vault data is equivalent to losing access to all stored accounts.

All Vaultwarden data is stored in the /opt/vaultwarden directory with the following structure:

/opt/vaultwarden
├── docker-compose.yml
├── npm-data
├── npm-letsencrypt
└── vw-data
    ├── db.sqlite3
    ├── db.sqlite3-shm
    ├── db.sqlite3-wal
    ├── rsa_key.pem
    └── tmp

SQLite has a WAL (Write-Ahead Logging) mechanism that requires a special approach for backup to ensure data consistency. Use the sqlite3 command with online backup mode.

Install sqlite3:

sudo apt install -y sqlite3

Create a backup bash script:

sudo nano /opt/vaultwarden/backup.sh

Enter the script:

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

BACKUP_DIR="/backup/vaultwarden"
DATA_DIR="/opt/vaultwarden/vw-data"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/vaultwarden_$DATE.tar.gz"

# Create backup directory if it doesn't exist
mkdir -p "$BACKUP_DIR"

# Backup SQLite database online (safe while container is running)
sqlite3 "$DATA_DIR/db.sqlite3" ".backup '$BACKUP_DIR/db_$DATE.sqlite3'"

# Create archive including database and attachments folder
tar -czf "$BACKUP_FILE" \
  -C "$BACKUP_DIR" "db_$DATE.sqlite3" \
  -C "$DATA_DIR" .

# Delete temporary db file
rm "$BACKUP_DIR/db_$DATE.sqlite3"

# Delete backups older than 30 days
find "$BACKUP_DIR" -name "vaultwarden_*.tar.gz" -mtime +30 -delete

echo "Backup completed: $BACKUP_FILE"
echo "Size: $(du -sh "$BACKUP_FILE" | cut -f1)"

Run the bash script:

# Give execute permission to the script
sudo chmod u+x /opt/vaultwarden/backup.sh

# Test run manually
sudo /opt/vaultwarden/backup.sh

Schedule automatic backup daily at 02:00 WIB:

# Edit root crontab
sudo crontab -e

# Add the following line:
0 2 * * * /opt/vaultwarden/backup.sh >> /var/log/vaultwarden-backup.log 2>&1

Verify Backup Integrity

A backup that has never been tested is equivalent to having no backup. Perform restore tests periodically, at least once every month:

# Extract backup to temporary directory
mkdir -p /tmp/vaultwarden-restore
tar -xzf /backup/vaultwarden/vaultwarden_YYYYMMDD_HHMMSS.tar.gz \
  -C /tmp/vaultwarden-restore

# Verify SQLite database integrity
sqlite3 /tmp/vaultwarden-restore/db_YYYYMMDD_HHMMSS.sqlite3 "PRAGMA integrity_check;"
# Correct output: "ok"

# Clean up temporary directory
rm -rf /tmp/vaultwarden-restore

Conclusion

Vaultwarden is a superior self-hosted password manager solution in terms of resource efficiency, feature completeness, and ease of management. You get the entire Bitwarden ecosystem through a Rust-based single-container architecture, including premium features that are usually paid, only at the cost of an entry-level VPS.

Related Posts