A password manager is the single piece of self-hosted infrastructure where the stakes are highest — and ironically, it's also one of the easiest to run. Vaultwarden is a Rust reimplementation of the Bitwarden server that runs in 200 MB of RAM, speaks the same API as the official server, and works with every official Bitwarden client (browser extension, mobile app, desktop, CLI). You point your existing Bitwarden app at your domain and it just works.

This guide covers the full setup: Vaultwarden in Docker, Nginx with HTTPS, automated backups, fail2ban for brute-force protection, and a clear-eyed look at the security model so you actually understand what you're trusting and what you aren't.

🐾 Wait — Bitwarden vs Vaultwarden?

Bitwarden Inc. publishes the official server, which is fine but heavy (multiple containers, ~2 GB RAM, runs Microsoft SQL Server). Vaultwarden is an unofficial reimplementation by dani-garcia that's API-compatible — same clients work — but built in Rust and runs in a single container with SQLite. Same security guarantees in practice, much lighter footprint. We're using Vaultwarden.

1. The security model in plain English

Before we touch a terminal, understand what you're actually doing. Vaultwarden uses client-side encryption: your master password never leaves your device, and your vault arrives at the server as a blob already encrypted with a key derived from that password. The server stores ciphertext. Even if someone steals the entire database, they get encrypted data and a salt — they still need to brute-force your master password.

What this means concretely:

2. Prepare the VPS

A small VPS is plenty — Vaultwarden uses ~50 MB of RAM with light load, and the database is tiny. The Starter plan is fine. Pick a location close to where you log in from most often.

SSH in as root, update, and create a non-root user:

apt update && apt upgrade -y
apt install -y ufw curl ca-certificates gnupg

ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable

adduser vault
usermod -aG sudo vault
rsync --archive --chown=vault:vault ~/.ssh /home/vault

Install Docker (same procedure as our other guides):

install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  tee /etc/apt/sources.list.d/docker.list > /dev/null

apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
usermod -aG docker vault

3. Deploy Vaultwarden with Docker

Switch to the vault user:

su - vault
mkdir -p ~/vaultwarden/data
cd ~/vaultwarden

Generate a secure admin token. Save this somewhere safe — you'll need it to access the admin panel later. We use the Argon2 hash format that Vaultwarden expects:

# Pick a strong token, then hash it
docker run --rm vaultwarden/server:latest /vaultwarden hash
# Paste your chosen plaintext token at the prompt
# Copy the output starting with $argon2id$...

Create ~/vaultwarden/compose.yaml:

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    volumes:
      - ./data:/data
    ports:
      - "127.0.0.1:8080:80"
    environment:
      DOMAIN: "https://vault.example.com"
      SIGNUPS_ALLOWED: "true"  # We'll flip this to false after creating accounts
      ADMIN_TOKEN: '$argon2id$v=19$m=65540,t=3,p=4$...'  # paste hash here
      WEBSOCKET_ENABLED: "true"
      ROCKET_PORT: "80"
      LOG_FILE: "/data/vaultwarden.log"
      LOG_LEVEL: "warn"

Note the port binding to 127.0.0.1:8080 — Vaultwarden is only reachable on localhost. Public access goes through Nginx, which gives us HTTPS, fail2ban hooks, and a single audit point.

docker compose up -d
docker compose logs --tail 20

You should see Rocket has launched from http://0.0.0.0:80. Test locally:

curl -I http://127.0.0.1:8080/alive
# HTTP/1.1 200 OK

4. Nginx reverse proxy + Let's Encrypt

Install Nginx and Certbot:

sudo apt install -y nginx certbot python3-certbot-nginx

Drop this config at /etc/nginx/sites-available/vaultwarden:

server {
    listen 80;
    server_name vault.example.com;

    # Strict TLS will be added by certbot

    client_max_body_size 525M;  # for attachments

    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;
    add_header Referrer-Policy "strict-origin" always;

    # Main app
    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 for live sync
    location /notifications/hub {
        proxy_pass http://127.0.0.1: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-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Enable and grab the cert:

sudo ln -s /etc/nginx/sites-available/vaultwarden /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d vault.example.com

Visit https://vault.example.com and you should land on the Bitwarden web vault login screen. The favicon, branding, everything looks like Bitwarden — that's because Vaultwarden serves the official Bitwarden web UI.

🐾 Need a VPS for self-hosting?

OliveVPS Starter at $3.99/mo handles Vaultwarden, Pi-hole, and a small Nextcloud instance comfortably on the same box. NVMe storage, no surprise bandwidth charges, and a free dashboard to swap between regions if you move.

Get Started →

5. Create your account and disable signups

Click "Create Account" on the web vault. Choose a strong master password — we recommend a passphrase (4+ random words from a dictionary, like "correct horse battery staple") rather than a typed-character password. Bitwarden's strength meter is reasonable; aim for "very strong."

After your account is created, immediately turn off public signups so randos can't register on your server. Edit compose.yaml:

      SIGNUPS_ALLOWED: "false"
docker compose up -d

If you want to add family members or a small team later, you can either temporarily flip signups back on, or invite them via the admin panel at https://vault.example.com/admin using the admin token you generated earlier.

6. Automated backups

This is the most important section. If your VPS dies and you don't have backups, your vault is gone. Vaultwarden uses SQLite by default, which means a clean backup is just a file copy of the data directory.

Create a backup script at /home/vault/backup-vaultwarden.sh:

#!/bin/bash
set -euo pipefail

BACKUP_DIR=/home/vault/backups
DATA_DIR=/home/vault/vaultwarden/data
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
ARCHIVE="$BACKUP_DIR/vaultwarden-$TIMESTAMP.tar.gz"

mkdir -p "$BACKUP_DIR"

# Use SQLite's online backup API for a consistent snapshot
sqlite3 "$DATA_DIR/db.sqlite3" ".backup '$DATA_DIR/db.sqlite3.bak'"

# Tar the data dir (excluding the live db, including the snapshot)
tar -czf "$ARCHIVE" \
  -C "$DATA_DIR/.." \
  --exclude='data/db.sqlite3' \
  --exclude='data/db.sqlite3-shm' \
  --exclude='data/db.sqlite3-wal' \
  data

# Clean up snapshot
rm -f "$DATA_DIR/db.sqlite3.bak"

# Keep last 14 days
find "$BACKUP_DIR" -name 'vaultwarden-*.tar.gz' -mtime +14 -delete

echo "Backup complete: $ARCHIVE"
sudo apt install -y sqlite3
chmod +x /home/vault/backup-vaultwarden.sh

Schedule it:

crontab -e
# Add this line:
0 3 * * * /home/vault/backup-vaultwarden.sh >> /home/vault/backup.log 2>&1

Now ship those backups off the VPS. A backup that lives only on the same server you're trying to protect against is not a backup. Use rclone to push to Backblaze B2, S3, or any object storage:

sudo apt install -y rclone
rclone config  # set up your remote
# Then add to the backup script after the tar line:
rclone copy "$ARCHIVE" remote-name:vaultwarden-backups/

Test by restoring to a different machine. Untested backups aren't backups.

7. Hardening: fail2ban, 2FA, admin panel

Fail2ban

Vaultwarden writes failed login attempts to its log file. Hook fail2ban to block IPs after 3 failures:

sudo apt install -y fail2ban

sudo tee /etc/fail2ban/filter.d/vaultwarden.conf <<'EOF'
[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
ignoreregex =
EOF

sudo tee /etc/fail2ban/jail.d/vaultwarden.conf <<'EOF'
[vaultwarden]
enabled = true
port = http,https
filter = vaultwarden
logpath = /home/vault/vaultwarden/data/vaultwarden.log
maxretry = 3
bantime = 86400
findtime = 600
EOF

sudo systemctl restart fail2ban

Enable 2FA on your account

Log into the web vault → Settings → Security → Two-step Login. Enable TOTP (use Aegis on Android or Raivo on iOS — avoid storing the TOTP secret in the same Vaultwarden instance, that defeats the purpose). Generate and save the recovery code somewhere offline — a password manager backup that requires the password manager to recover is a deadlock.

Lock down the admin panel

The admin panel at /admin is gated by your ADMIN_TOKEN, but you can also restrict it to your IP only at the Nginx level for extra defense:

location /admin {
    allow YOUR.HOME.IP.ADDRESS;
    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;
}

8. Connect Bitwarden clients

The official Bitwarden apps are completely Vaultwarden-compatible — you just point them at your server.

Browser extension: Install Bitwarden from your browser's extension store. Before logging in, click the gear icon → choose "Self-hosted" → enter https://vault.example.com. Save. Then log in normally.

Mobile app: Same flow. On the login screen, tap the gear icon top-left → "Self-hosted" → enter your URL. Then log in.

Desktop app: Same. Settings → Self-hosted environment → enter the URL.

CLI:

npm install -g @bitwarden/cli
bw config server https://vault.example.com
bw login

FAQ

Is Vaultwarden as secure as official Bitwarden?

For the encryption itself — yes. The vault payload is encrypted on your device before transmission, and Vaultwarden faithfully implements the Bitwarden protocol. The official server has had third-party audits; Vaultwarden has not had a formal audit but the code is open-source, actively reviewed, and used by tens of thousands. The trade-off is that you are now responsible for the operational security — patching, backups, and TLS — that Bitwarden would handle for you on their hosted plan.

What happens if my VPS goes down?

Bitwarden clients cache the vault locally. If the server is unreachable, you can still read existing entries — you just can't add new ones or sync to other devices until it's back. This makes occasional VPS hiccups painless. What you can't survive is data loss without backups, which is why section 6 matters.

Can I migrate my existing Bitwarden vault?

Yes. In your existing Bitwarden web vault, go to Tools → Export Vault → JSON (encrypted). On your new Vaultwarden instance, log in via the web vault and Tools → Import → Bitwarden (json). Folders and items transfer cleanly. Two-factor configurations don't transfer; you'll need to re-enable 2FA on the new server.

How much does it cost compared to Bitwarden Premium?

Bitwarden Premium is $10/year — genuinely cheap. Self-hosting is more about control than cost. A VPS at $3.99/mo runs you ~$48/year, plus the time you spend on backups and updates. The wins are: full control, your data stays on your hardware, you can host as many users as you want for free, and unlimited file attachments without upgrading. The losses are: you're responsible for uptime and security, and if you mess up backups, you lose everything.

Does Vaultwarden support organizations and sharing?

Yes — fully. Create an organization through the web vault, invite users, set up collections for shared logins. All organization features that the official Bitwarden server gates behind the paid Teams or Enterprise plans (groups, collections, fine-grained permissions) are unlocked in Vaultwarden by default.

What happens if Vaultwarden development stops?

Worst case, you can export your vault from any Bitwarden client and import it into another password manager — KeePassXC, 1Password, the official Bitwarden cloud, anything. Your data is never locked into the server software. The Bitwarden API is stable and Vaultwarden has been actively maintained for years; this risk is low but worth knowing.

🐱
OliveVPS Team

Half of us run Vaultwarden in production. The hardening recommendations here come from what's actually held up against the noise.