← Back to Blog

Vaultwarden on a VPS: Your Own Password Manager for Family or Team

Published · by RS Computers

Vaultwarden Password manager Docker

Vaultwarden is a small, free server that works with the official Bitwarden apps, so you can run your own password manager for a family or a small team on a VPS and keep using the Bitwarden browser extension, phone apps and desktop app. You need Docker, one compose file, a domain with HTTPS, and a nightly backup that you have restored at least once. In our lab, Vaultwarden used under 30 MiB of RAM with two users and 30 saved logins, so the smallest Linux plan is enough for a household. The family setup comes first, then the extra steps a team needs.

Key facts, checked on 9 October 2026:

Vaultwarden, Bitwarden and the words you will see

Bitwarden makes open-source password manager apps for browsers, phones, desktops and the command line, and they can connect to any compatible server. Vaultwarden is an independent, much lighter rewrite of that server in Rust, and it includes the paid features (organizations, attachments, YubiKey and FIDO2 two-step login, emergency access) without a licence.

The vault is your encrypted list of logins, and the web vault is the website version of the app. An organization is a shared space, and a collection is a folder inside it for chosen people. Your master password never leaves your device: the apps encrypt everything before upload, so the server stores only scrambled data and the admin cannot reset a lost master password.

You also become the support desk. If the server is down, people can still read the copy cached in their apps but cannot sync, and Bitwarden support does not help with Vaultwarden servers.

What it costs: self-hosted compared with Bitwarden and 1Password plans

The table uses each vendor's official price page, in US dollars, checked on 9 October 2026. Prices change, so follow the links before you decide.

OptionFamily of 5, per yearTeam of 10, per yearWho runs the server
Bitwarden Families / Teams$47.88 (up to 6 users)$480 (Teams, $4 per user per month)Bitwarden
Bitwarden EnterpriseNot aimed at families$720 ($6 per user per month)Bitwarden, or you with a licence
1Password Families / Teams Starter Pack$53.88 first year, then $71.88$299.40 (Starter Pack, 10 to 20 users)1Password
1Password BusinessNot aimed at families$1,078.80 ($8.99 per user per month)1Password
Vaultwarden on your VPS$0 for the software, plus the server$0 for the software, plus the serverYou

Bitwarden also has a free organization, but it is limited to "two users" and "two collections" (Bitwarden help). For one family, the hosted price is hard to beat. Self-hosting wins when the team grows, when everyone should get the premium features, or when the server already runs other things, such as Nextcloud. The real cost is your time: about an hour to set up, then a few minutes a month for updates and a restore test.

How much server does Vaultwarden need? Our lab numbers

We ran the whole setup below in a Debian 13 container (2 vCPUs, 3 GB RAM) on one of our Amsterdam servers, with Docker 29.9.0, Caddy 2.11.7 and Vaultwarden 1.37.3, later updated to 1.37.4. Measured with docker stats and free -m:

MeasurementResult in our lab
Vaultwarden RAM right after start8.6 MiB
Vaultwarden RAM with 2 users, 30 logins, web vault and CLI in use23 to 30 MiB
1,000 vault syncs, 25 at a time, through HTTPS11.2 seconds, all answered; peak 72.7 MiB
Caddy (HTTPS proxy) RAM17 MiB idle, 47 MiB after the load test
Whole machine: Docker, Caddy, Vaultwarden, fail2ban, cron228 MB used
Vaultwarden image on diskAbout 400 MB unpacked (90 MB download)
Data folder with 30 logins / backup with a 200 kB attachment1.2 MB / 235 KB

A family vault fits easily on VPS Nano (1 vCPU, 1 GB RAM, 20 GB NVMe). For a team, or a server that also hosts other apps, take VPS Micro (2 vCPU, 2 GB) or VPS Mini (4 vCPU, 4 GB). Every plan on our VPS plans page has NVMe storage, unmetered traffic and its own IPv4 and IPv6, and upgrades later from the client area with a short reboot. Pick the city closest to your users: Amsterdam, Dublin or Prishtina. Availability by city is on the plans page.

Part 1: the family setup, step by step

Before you start, point a DNS name such as vault.example.com to your server's IPv4 (an A record) and IPv6 (an AAAA record). Our reverse proxy and SSL guide explains DNS records and the ports that must be open for Let's Encrypt.

Step 1: install Docker and create a user for the vault

# as root
apt-get update
apt-get install -y curl ca-certificates sqlite3
curl -fsSL https://get.docker.com | sh
useradd -m -s /bin/bash vault
usermod -aG docker vault
id vault

The last line should print uid=1000(vault) gid=1000(vault) groups=1000(vault),989(docker). Write down the uid; the compose file uses it. Members of the docker group can control the whole machine, so treat the vault user like an admin account. Then switch to it with su - vault. If Docker is new to you, our Docker on a VPS guide explains the basics.

Step 2: create the admin password, stored as a hash

Vaultwarden has an admin page for inviting people and changing settings. Its password should be stored as an Argon2 hash, so a stolen config file does not reveal it.

# as the vault user
mkdir -p ~/vaultwarden/vw-data
cd ~/vaultwarden
docker run --rm -it vaultwarden/server:1.37.4 /vaultwarden hash --preset owasp

Type a long admin password twice. You get one line like this (shortened here):

# output
ADMIN_TOKEN='$argon2id$v=19$m=19456,t=2,p=1$vfnqpaduNN2d...$LfvLQY/rb9xS...'
Generation of the Argon2id PHC string took: 91.829244ms

Copy that whole line into a file named .env and lock it down. The single quotes matter: they stop Docker Compose from treating the $ signs as variables, so you do not need the $$ escaping that older guides show.

# as the vault user, in ~/vaultwarden
cat > .env <<'EOF'
ADMIN_TOKEN='paste-the-value-from-your-own-output-here'
EOF
chmod 600 .env

Step 3: write the compose file and the Caddyfile

Create ~/vaultwarden/docker-compose.yml with the content below. Change the domain, and change 1000:1000 if your uid was different.

# as the vault user: the whole of ~/vaultwarden/docker-compose.yml
services:
  vaultwarden:
    image: vaultwarden/server:1.37.4
    container_name: vaultwarden
    restart: unless-stopped
    user: "1000:1000"
    env_file: .env
    environment:
      DOMAIN: "https://vault.example.com"
      SIGNUPS_ALLOWED: "false"
      ROCKET_PORT: "8080"
      LOG_FILE: "/data/vaultwarden.log"
    volumes:
      - ./vw-data:/data
    ports:
      - "127.0.0.1:8080:8080"

  caddy:
    image: caddy:2
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./caddy-data:/data

SIGNUPS_ALLOWED: "false" means nobody can create an account without an invitation. user and ROCKET_PORT run Vaultwarden as your normal user instead of root, so the backup script can read its files. The 127.0.0.1 port is only for checks from the server itself.

# as the vault user: the whole of ~/vaultwarden/Caddyfile
vault.example.com {
	reverse_proxy vaultwarden:8080 {
		header_up X-Real-IP {remote_host}
	}
}

With a public domain, Caddy requests a Let's Encrypt certificate by itself. Our lab has no public domain, so we added tls internal (Caddy's own test certificate) and bound port 443 to 127.0.0.1; everything else was identical. The header_up line passes the visitor's IP address to Vaultwarden, and Part 2 shows why that matters.

Step 4: start it and check

# as the vault user, in ~/vaultwarden
mkdir -p caddy-data
docker compose up -d
curl -s http://127.0.0.1:8080/alive
docker compose ps

The alive check answers with a timestamp such as "2026-10-09T02:22:27.733035Z", and both containers show Up. The data folder now holds db.sqlite3 (the database with all vaults) and rsa_key.pem (the key that signs logins).

Can you skip HTTPS for a quick test? We tried: the web vault bundled with 1.37 refused to create an account over plain HTTP, even on http://localhost, with the error "Insecure URL not allowed. All URLs must use HTTPS." It needs browser crypto features that only work on HTTPS (Vaultwarden wiki).

Step 5: invite yourself, then the family

  1. Open https://vault.example.com/admin and log in with the password you typed in step 2, not the hash.
  2. Under Users, use Invite User with your own email address.
  3. Open https://vault.example.com, choose Create account, enter the same address, and set your master password. Make it a long passphrase and write it on paper for the family safe.
  4. Invite each family member the same way.

Without email set up, nothing is sent: the invited person opens the site and signs up with the invited address. A sign-up from an address we had not invited was rejected with "Registration not allowed or user already exists" in the log.

Step 6: one shared organization for the family

Personal vaults are private. For shared logins such as Wi-Fi or streaming, create an organization: in the web vault choose New organization, give it a name and submit. It comes with a Default collection. Under Members, use Invite member, pick the role User and choose the collections they may see.

Membership has three stages: Invited, then Needs confirmation once the person has an account, and only then can the owner click Confirm. That last step exists because the owner's app encrypts the organization key for the new member, so the server never holds it readable. Our lab member went through exactly those stages.

Step 7: connect the Bitwarden apps

Install the official Bitwarden app or browser extension. On the login screen, change the server from bitwarden.com to self-hosted and enter https://vault.example.com. We tested logins with the web vault and the official Bitwarden CLI 2026.9.1, including saving 30 items and an attachment. Turn on two-step login for every account.

Backups you have actually restored

Copying db.sqlite3 while Vaultwarden writes to it can give you a broken file. SQLite's .backup command makes a consistent copy of a live database, which is what the Vaultwarden backup guide recommends. Besides the database you need rsa_key.pem, the attachments folder and config.json if the admin page created one. Save this as ~/vaultwarden/backup.sh:

# as the vault user: the whole of ~/vaultwarden/backup.sh
#!/bin/sh
set -e
cd /home/vault/vaultwarden
STAMP=$(date +%Y%m%d-%H%M)
mkdir -p backups/tmp
sqlite3 vw-data/db.sqlite3 ".backup 'backups/tmp/db.sqlite3'"
sqlite3 backups/tmp/db.sqlite3 "PRAGMA integrity_check;"
tar -czf "backups/vw-$STAMP.tar.gz" \
  --exclude='./db.sqlite3*' --exclude='./icon_cache' --exclude='./tmp' --exclude='./vaultwarden.log' \
  -C vw-data . -C ../backups/tmp db.sqlite3
rm backups/tmp/db.sqlite3
find backups -name 'vw-*.tar.gz' -mtime +14 -delete
echo "backup ok: backups/vw-$STAMP.tar.gz"

Make it executable, run it once, and schedule it for 03:15 every night. Debian images without cron need apt install -y cron as root first.

# as the vault user, in ~/vaultwarden
chmod +x backup.sh
./backup.sh
(crontab -l 2>/dev/null; echo "15 3 * * * /home/vault/vaultwarden/backup.sh >> /home/vault/vaultwarden/backups/backup.log 2>&1") | crontab -
crontab -l

The script prints ok from the integrity check and then backup ok: backups/vw-20261009-0252.tar.gz. The archives still sit on the same server, so copy them elsewhere every night with our off-site backup guide using restic, and keep them private: they hold email addresses and the signing key.

The restore drill

We pretended the data folder was lost and restored it from the archive:

# as the vault user, in ~/vaultwarden
docker compose stop vaultwarden
mv vw-data vw-data.old
mkdir vw-data
tar -xzf backups/vw-20261009-0252.tar.gz -C vw-data
docker compose start vaultwarden
sqlite3 vw-data/db.sqlite3 "select count(*) from ciphers;"

The restore took 1 second and the item count was 30 before and after. Then we logged in with a client, downloaded the attachment and compared its SHA-256 checksum with the original file: identical. Always extract into an empty folder: an old db.sqlite3-wal file next to a restored database can corrupt it. Repeat the drill monthly.

Updating without breaking the apps

Bitwarden ships new app versions every month, and sometimes they need a newer server. Our lab hit exactly that: a fresh login with the Bitwarden CLI 2026.9.1 against Vaultwarden 1.37.3 failed with a "user key id backfill" error, because the older server did not know a new API call. After updating to 1.37.4 the same login worked and all 30 items and the attachment were there. These are the update commands we ran; put your current and new version numbers in the sed line:

# as the vault user, in ~/vaultwarden
./backup.sh
sed -i 's#vaultwarden/server:1.37.3#vaultwarden/server:1.37.4#' docker-compose.yml
docker compose pull vaultwarden
docker compose up -d
docker logs vaultwarden 2>&1 | grep -m1 Version

The update took 2 seconds of downtime in our lab and the log showed Version 1.37.4. Watch the release page for security fixes. A pinned version never changes behind your back, which is why we prefer it to the latest tag.

Part 2: extra steps for a small team

Email invitations over port 587

With email set up, invitations arrive as a "Join Vaultwarden" message and people can use email two-step login. You do not need your own mail server: use your mail provider's SMTP login on the submission port 587 with STARTTLS. Outbound port 25 is blocked on new RS Computers servers; it matters only for a full mail server such as mailcow, and we open it on request through Telegram. The settings go under environment: in the compose file. The values for port 587 below follow the official wiki:

# as the vault user: add under environment: in docker-compose.yml
      SMTP_HOST: "smtp.your-provider.com"
      SMTP_PORT: "587"
      SMTP_SECURITY: "starttls"
      SMTP_FROM: "vault@example.com"

Put SMTP_USERNAME=... and SMTP_PASSWORD=... in the .env file, not in the compose file, then run docker compose up -d. For port 465 the matching setting is force_tls (SMTP wiki). We tested the same variable names against a local mail catcher (with SMTP_SECURITY set to off): the admin page's test mail and a user invitation both arrived. A real provider on 587 was not part of the lab.

Collections and roles instead of one big shared folder

Create one collection per area, for example Servers, Social media and Finance, and give each member only what they need. The roles in the invite dialog are User (use and add items in assigned collections), Admin (manage access and all collections), Owner (everything, including deleting the organization) and Custom. Keep two owners so one holiday does not lock the team out. When someone leaves, use Revoke access or Remove, then change the passwords they could see. Vaultwarden also supports policies such as required two-step login, and an event log that is turned on with ORG_EVENTS_ENABLED: "true" according to the project's settings template; we did not test those two in this lab.

Real client IPs, rate limits and fail2ban

Without the header_up X-Real-IP line from step 3, every failed login in the log shows Caddy's internal address. In our lab that was IP: 172.18.0.3; with the line it became the address Caddy saw the client connect from. That matters for fail2ban, a tool that reads logs and blocks addresses that fail too often. Vaultwarden already slows guessing: after a short burst of wrong passwords our test client got HTTP 429 (too many requests). Fail2ban is an optional extra layer:

# as root
apt install -y fail2ban
cat > /etc/fail2ban/filter.d/vaultwarden.local <<'EOF'
[INCLUDES]
before = common.conf

[Definition]
failregex = ^.*?Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
ignoreregex =
EOF
cat > /etc/fail2ban/jail.d/vaultwarden.local <<'EOF'
[vaultwarden]
enabled = true
port = 80,443
filter = vaultwarden
banaction = iptables-allports
chain = FORWARD
logpath = /home/vault/vaultwarden/vw-data/vaultwarden.log
maxretry = 5
findtime = 15m
bantime = 4h
EOF
systemctl restart fail2ban
fail2ban-client status vaultwarden

Traffic to Docker containers passes the FORWARD chain, and Debian defaults to nftables while Docker uses iptables, hence those two lines, as the fail2ban wiki page advises. In our lab fail2ban 1.1.0 found the failed logins, and a test ban landed at the top of the FORWARD chain, ahead of Docker's rules. Add your office IP to ignoreip so you never ban yourself.

Close the admin page when you are done

Once the team is invited, comment out the ADMIN_TOKEN line in .env with a # and run docker compose up -d. The admin page then only shows "The admin panel is disabled". Remove the # again when you need it. To keep the vault off the open internet, put it behind a WireGuard VPN, and watch it with Uptime Kuma so you hear about downtime first.

Frequently asked questions

Is Vaultwarden safe to use for passwords?

Vaultwarden stores only data the Bitwarden apps encrypted on the user's device, so the server never sees master passwords or readable items. The risks are an outdated version, a weak admin password or missing backups; version 1.37.4 alone fixed seven security advisories.

Do the official Bitwarden apps work with Vaultwarden?

Yes. The browser extensions, desktop apps, mobile apps and CLI connect when you choose a self-hosted server and enter your URL. New app versions sometimes need a newer server: Vaultwarden 1.37.0 was required for clients 2026.7.0 and later, so update the server regularly.

How much RAM does Vaultwarden use?

In our lab, Vaultwarden 1.37 used 8.6 MiB right after start, 23 to 30 MiB with two users and 30 items, and 72.7 MiB at peak during 1,000 syncs. The whole server with Docker and Caddy used 228 MB, so a 1 GB VPS is enough for a family.

Can I run Vaultwarden without a domain or HTTPS?

Not usefully. The Bitwarden web vault needs HTTPS for its browser cryptography, and in our test it refused to create an account even over http://localhost. Use a domain with a Let's Encrypt certificate, or reach the server through a VPN with a certificate for an internal name.

How do I stop strangers from signing up on my Vaultwarden?

Set SIGNUPS_ALLOWED to false and invite people from the admin page or from an organization. Invited addresses can still create an account, while any other address gets "Registration not allowed".

What happens if I forget my master password?

The vault cannot be decrypted without it, and the server admin cannot reset it. Organizations can use admin password reset for members enrolled in advance; otherwise the vault is lost, so keep the master password on paper somewhere safe.

Where to run your vault

Everyone in the house or office uses the vault every day, yet it fits on our smallest server. Choose a VPS plan in Amsterdam, Dublin or Prishtina, follow the seven steps, and do the restore drill before you move everyone over. If you would rather have us install it, setup work is quoted per job: message us on Telegram or email us with your domain and the number of users.

← All articles

Chat on Telegram