Skip to content
+1 (813) 212-3723 support@layeronecloud.com
Self-hosting VPS guides

How to host Vaultwarden on a VPS securely

Build a private Vaultwarden VPS with Docker, Caddy HTTPS, closed registration, two-layer firewall rules, tested backups, and safe upgrades.

7 min read

Vaultwarden is a lightweight, community-built server compatible with Bitwarden clients. A Vaultwarden VPS can be inexpensive, but it becomes some of the most sensitive infrastructure you operate: it stores encrypted vaults, account metadata, attachments, recovery material, and sessions. The correct starting point is HTTPS, closed public registration, strong account MFA, and a tested off-server backup.

Vaultwarden is not the official Bitwarden server and is not operated or supported by Bitwarden. Review its project status and license before using it for an organization or regulated data.

Vaultwarden VPS requirements and LayerOne cost

Item Practical starting point
Minimum RAM 1 GB for a personal instance with SQLite
Recommended RAM 2 GB for OS and container update headroom and several active users
Expected CPU usage Usually below 2% of one vCPU at idle; brief 10–40% bursts during login, sync, key derivation, and large attachment transfers
Storage requirement 10 GB minimum; 20 GB or more if users store attachments or Sends
Exact LayerOne SKU gc.micro — catalog slug layerone-starter, 1 vCPU, 2 GB RAM, 40 GB disk
Expected monthly cost $5.00/month equivalent, metered at $0.0068/hour while the VPS exists

Vaultwarden does not publish a universal RAM or CPU minimum. These are conservative operating estimates for the Compose stack below, not performance guarantees. User count, Argon2 settings, attachments, icons, audit activity, and backup jobs change the result. Measure the real workload with docker stats and the VPS monitoring guide.

LayerOne bills by the hour. The monthly equivalent assumes a continuously existing server and excludes optional services and usage that varies. Check Pricing for the current catalog before deployment.

Before you install

This guide targets a fresh Ubuntu 24.04 LTS VPS. Complete Your first hour on a new server, install Docker from Running Docker, and create a DNS A record such as vault.example.com pointing to the VPS.

Use a dedicated hostname. Bitwarden web clients require a secure browser context, so a raw public http://SERVER_IP deployment is not a usable or safe production endpoint.

1. Start with a closed firewall

In the LayerOne Networking tab, use inbound DROP and outbound ACCEPT. Initially allow TCP 22 only from your administration address. Add public TCP 80 and 443 only after the first account has been created locally. See The cloud firewall.

Mirror the policy in UFW:

sudo apt update
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from YOUR.ADMIN.IP.ADDRESS to any port 22 proto tcp
sudo ufw enable
sudo ufw status verbose

Keep the browser console open until a new SSH login works. Docker can bypass UFW for published container ports, so the LayerOne cloud firewall remains essential. The temporary Vaultwarden port in this guide binds to 127.0.0.1 and is never public.

2. Create the Compose project

sudo mkdir -p /opt/vaultwarden/data
sudo chown -R "$USER":"$USER" /opt/vaultwarden
cd /opt/vaultwarden

Create /opt/vaultwarden/compose.yaml:

services:
  vaultwarden:
    image: vaultwarden/server:latest
    restart: unless-stopped
    environment:
      DOMAIN: "https://vault.example.com"
      SIGNUPS_ALLOWED: "true"
    volumes:
      - ./data:/data
    ports:
      - "127.0.0.1:8000:80"
    expose:
      - "80"

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    depends_on:
      - vaultwarden
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config

volumes:
  caddy_data:
  caddy_config:

Replace vault.example.com with the hostname you control. The official project publishes its supported stable container as vaultwarden/server:latest; review release notes and pin a tested version if your change policy requires explicit versions.

Create /opt/vaultwarden/Caddyfile:

vault.example.com {
    encode zstd gzip
    reverse_proxy vaultwarden:80
}

Validate the stack, but start only Vaultwarden for now:

cd /opt/vaultwarden
docker compose config --quiet
docker compose up -d vaultwarden
docker compose logs --tail=100 vaultwarden

3. Create the first account without public registration

From your own computer, open an SSH tunnel to the loopback-only port:

ssh -L 8000:127.0.0.1:8000 admin@SERVER_IP

Leave that session open and visit http://localhost:8000 in the local browser. localhost is treated as a secure development context by modern browsers. Register the one intended owner account, sign in, and confirm the vault opens.

Then edit compose.yaml and change:

SIGNUPS_ALLOWED: "false"

Do not set ADMIN_TOKEN unless you actually need the separate Vaultwarden admin panel. Omitting it keeps that privileged interface disabled. Validate and recreate the service:

cd /opt/vaultwarden
docker compose config --quiet
docker compose up -d vaultwarden
docker compose logs --tail=50 vaultwarden

The registration form may still appear in clients, but the server must reject new accounts after SIGNUPS_ALLOWED=false is active.

4. Publish HTTPS and verify the boundary

Now add TCP 80 and 443 from 0.0.0.0/0 to the LayerOne cloud firewall and UFW:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status verbose
cd /opt/vaultwarden
docker compose up -d
docker compose ps
docker compose logs --tail=100 caddy vaultwarden

Open https://vault.example.com, sign in as the existing user, install a Bitwarden client, and perform a two-way test: add a disposable item on one client, sync it to another, edit it, and sync back.

Confirm TLS and socket exposure:

curl -fsSI https://vault.example.com
sudo ss -lntp | grep -E ':(80|443|8000)[[:space:]]'
docker compose ps

Port 8000 should appear only on 127.0.0.1; 80 and 443 are the only public web ports. Enable two-step login for every vault user and save recovery codes away from the VPS and away from the vault they recover.

5. Back up everything needed for recovery

A client export is useful, but it is not a server backup: it may omit attachments, organizations, Sends, and server configuration. The simplest consistent small-instance backup is a brief stop and an archive of the whole data directory:

cd /opt/vaultwarden
docker compose stop vaultwarden
sudo tar -C /opt/vaultwarden -czf "/root/vaultwarden-$(date -u +%Y%m%dT%H%M%SZ).tar.gz" data compose.yaml Caddyfile
docker compose start vaultwarden

Encrypt the archive and move it off the VPS immediately. Vaultwarden's data can include plaintext account email addresses and two-factor material as well as encrypted vault content. Store the backup password or key separately. LayerOne does not back up the VPS; see Backups and snapshots.

For a restore, deploy the same or a compatible Vaultwarden version, stop the container, replace /opt/vaultwarden/data with the saved directory, preserve its ownership and permissions, and start Vaultwarden. Test login, attachment download, client sync, organization access, and MFA before calling the restore successful. Perform this test on an isolated hostname so clients cannot write to both copies.

6. Update without risking the vault

Read Vaultwarden's release notes, make and copy off a fresh backup, then:

cd /opt/vaultwarden
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 vaultwarden

Verify the reported version, browser login, client sync, attachment access, and one new disposable item. Do not prune the previous image or delete the backup until those checks pass. Keep Ubuntu and Docker patched as separate maintenance tasks.

Security checklist

  • Keep SIGNUPS_ALLOWED=false except for a deliberately controlled enrollment window. Prefer invitations for additional users.
  • Never use DISABLE_ADMIN_TOKEN=true; it makes the admin panel unauthenticated.
  • Use HTTPS only, a unique long master password, account MFA, and recovery codes stored offline.
  • Treat anyone with root access, Docker control, or backup access as a highly privileged operator even though vault item bodies are encrypted.
  • Do not expose a database port. SQLite stays inside /data in this design.
  • Configure SMTP if you depend on invitations, email verification, or event notices, and test delivery without weakening TLS validation.
  • Monitor free space and backup growth. Attachments can consume the 40 GB disk much faster than vault text records.

Official and LayerOne references

Still stuck

Chat with us from the portal.

Ask the assistant from the Chat bar. During business hours you can ask for a person and a human joins live.