How to Self-Host a Password Manager with Vaultwarden

Why Host Your Own Password Manager

Most people store passwords in a browser or a free cloud vault and never think about who holds the keys. That works until it doesn’t: a provider raises prices, changes its privacy terms, or suffers a breach you can’t control. Self-hosting Vaultwarden puts the encrypted database on hardware you own, behind a domain you control, with no third party in the middle.

Vaultwarden is a lightweight, open-source reimplementation of the Bitwarden server API. It is written in Rust, licensed under AGPL-3.0 (the source lives at the Vaultwarden project on GitHub), and designed to run on the smallest VPS you can rent. It speaks the same protocol as official Bitwarden clients, so your phone, browser extension, and desktop app keep working without modification.

Smartphone showing privacy and security settings, relevant to self-hosting a password manager

What You Need Before Starting

This guide assumes a Linux server (Ubuntu 22.04 or newer) with Docker already installed, a domain name pointing at the server, and a way to obtain a TLS certificate. You can follow our separate walkthroughs to set up Docker with Portainer and to set up Nginx Proxy Manager as a reverse proxy.

You also need a reverse proxy that terminates HTTPS. The web vault relies on the browser Web Crypto API, which only functions in a secure context. The official Bitwarden apps refuse to connect to a server that isn’t served over HTTPS, so plain HTTP on port 80 will not work. Vaultwarden recommends a reverse proxy over its built-in TLS for easier certificate handling.

Resource requirements are modest. The container idles at roughly 10 to 20 MB of RAM and runs comfortably on a VPS with 512 MB of memory and a single shared CPU. That is the entire point of the project: the official Bitwarden server is far heavier and overkill for one person or a family.

Step 1: Create the Compose File

Create a project directory and write a compose.yaml. The configuration below mounts a host folder for persistent storage, binds the container to localhost only (the proxy reaches it on 127.0.0.1:8000), and sets the public domain Vaultwarden reports to clients. The image is published to Docker Hub and ghcr.io; pin a specific tag in production instead of latest if you want reproducible deployments.

Open the file with your editor and paste the following, replacing the domain with yours:

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    environment:
      DOMAIN: "https://vw.yourdomain.com"
      SIGNUPS_ALLOWED: "false"
      WEBSOCKET_ENABLED: "true"
    volumes:
      - ./vw-data/:/data/
    ports:
      - 127.0.0.1:8000:80

Bind to 127.0.0.1 rather than 0.0.0.0 so the raw container is never exposed to the internet. The proxy is the only thing that should face the public. Lock down the host with UFW firewall rules and SSH hardening before you open any port.

Step 2: Enable the Admin Panel and Signups

Start the container once with signups open, then create your account, then close signups. Generate a strong admin token first:

openssl rand -base64 48

Add the resulting string to the environment block as ADMIN_TOKEN. Vaultwarden accepts a plain string but prefers an Argon2 PHC hash for the admin token, which you generate from that same value. The admin panel at /admin lets you invite users, disable signups, and inspect active sessions without touching the database directly.

A typical hardened environment block looks like this:

environment:
  DOMAIN: "https://vw.yourdomain.com"
  SIGNUPS_ALLOWED: "false"
  INVITATIONS_ALLOWED: "false"
  ADMIN_TOKEN: "paste-your-generated-token-here"
  WEBSOCKET_ENABLED: "true"

Keep signups off after you and your family have registered. If you ever need to add someone, flip SIGNUPS_ALLOWED to true, restart, register, then flip it back. The admin panel’s invite function is the cleaner route for trusted users.

Step 3: Put It Behind HTTPS

Point your reverse proxy at 127.0.0.1:8000 and request a TLS certificate for the subdomain. With Nginx Proxy Manager this is a few clicks: add a proxy host for vw.yourdomain.com, set the forward hostname to vaultwarden (if both are on the same Docker network) or 127.0.0.1, port 8000, and enable the SSL certificate with the force-HTTP-to-HTTPS switch on.

Vaultwarden streams live updates to clients over a WebSocket. Enable the WebSocket support in both the container (WEBSOCKET_ENABLED=true) and your proxy configuration so vault changes appear instantly across devices instead of on the next manual sync. Without it, saving a password on your phone may not show up on your laptop until you refresh.

Step 4: Connect the Bitwarden Clients

Install the official Bitwarden apps from bitwarden.com/download on every device. In each client’s settings, change the server URL to your subdomain, log in with the account you created, and you are done. There is no forked or modified client to install; Vaultwarden is server-side only and the stock apps talk to it natively.

Turn on two-factor authentication immediately. Vaultwarden supports TOTP authenticator apps, email codes, FIDO2 WebAuthn security keys, and YubiKey. The admin panel can also enforce a two-step login policy for every user. A self-hosted vault with one weak password defeats the entire exercise, so 2FA is not optional here.

Backups: The Part People Skip

Self-hosting means you own the risk. All vault data lives in the mounted ./vw-data/ folder: a SQLite database, any file attachments, and the config. Back that folder up on a schedule to a separate location. Our guide to backing up a VPS with Restic and Backblaze B2 fits this exactly: point Restic at vw-data/ and run it daily. Test a restore once so you know the backup is real.

How often you back up depends on how often you change passwords. A daily snapshot is enough for most people, and because the database is small, each backup costs almost nothing. Stop the container or use a tool that copies a consistent snapshot if you worry about a write happening mid-copy; SQLite handles concurrent reads well, but a paused backup removes the edge case entirely.

Also export an encrypted JSON from the web vault as an offline copy. It is your escape hatch if the server dies and the backups are somehow unreadable. Store it somewhere you would keep other sensitive documents.

Vaultwarden vs the Official Bitwarden Server

Both serve the same clients. The difference is footprint and control. The table below shows where each fits.

FactorVaultwardenOfficial Bitwarden Server
LanguageRustC# (.NET)
RAM at idle~10 to 20 MBHundreds of MB plus dependencies
DatabaseSQLite by default (MySQL/Postgres optional)MSSQL (heavier stack)
Best forIndividuals, families, small teamsLarger organizations needing the full enterprise feature set
Setup effortOne containerMulti-container stack

For a personal vault, Vaultwarden is the pragmatic choice. The official server only makes sense when you need its enterprise administration features, and even then you are managing far more infrastructure.

Lock Down the Server After Install

Getting the container running is the easy part. Keeping it safe is the habit that matters. Start with the network: only the reverse proxy should accept public traffic, so your UFW rules should allow 80, 443, and your SSH port, then drop everything else. The Vaultwarden container already binds to 127.0.0.1, so even if a rule slips, the raw service stays off the internet.

Next, protect SSH. Disable password authentication, require key-based login, and consider moving the service to a non-standard port or fronting it with a tool that blocks repeated failed attempts. Our guides on SSH hardening and monitoring with Uptime Kuma cover the specifics. Keep the host and the Vaultwarden image patched: newer releases fix security issues, and a stale container is the most common way self-hosted services get owned.

Finally, separate your secrets from your vault. The admin token and any API keys belong in an environment file that is not committed to version control, and the backup target should be a different provider than your VPS. If both live in one account and that account is compromised, your redundancy disappears.

Frequently Asked Questions

Is my data encrypted if the server is compromised? Yes. Bitwarden clients encrypt your vault with your master password before anything leaves the device. Vaultwarden stores only ciphertext. A server breach exposes encrypted blobs, not your passwords, as long as your master password is strong.

Can I use it with multiple people? Yes. You can enable organizations for secure sharing, or simply invite family members and share individual items. Each user gets their own encrypted vault.

What happens if I forget the admin token? You can regenerate it by editing the compose environment and restarting the container. The vault data is unaffected because the token only gates the admin panel, not the encrypted database.

Do I need a static IP? No. A dynamic IP is fine as long as your domain’s DNS points to it and your proxy handles the certificate. Most VPS providers give a stable address anyway.

Final Thoughts

Self-hosting a password manager sounds intimidating until you see it is one container, a reverse proxy, and a backup job. Vaultwarden keeps the experience of a polished commercial product while removing the dependency on someone else’s infrastructure. Set it up on a small VPS, connect the apps you already use, switch on two-factor authentication, and automate the backup. You end up with a password manager that is cheaper, lighter, and fully yours.

Irfan is a Creative Tech Strategist and the founder of Grafisify. He spends his days testing the latest AI design tools and breaking down complex tech into actionable guides for creators. When he’s not writing, he’s experimenting with generative art or optimizing digital workflows.

Leave a Reply

Your email address will not be published. Required fields are marked *

You might also like
How to Speed Up a Slow WordPress Site

How to Speed Up a Slow WordPress Site

GitHub Actions Cache: Cut CI Time and Cost

GitHub Actions Cache: Cut CI Time and Cost

Alembic Database Migrations in FastAPI: Production Guide

Alembic Database Migrations in FastAPI: Production Guide

DuckDB vs SQLite: Best Embedded Database for Python

DuckDB vs SQLite: Best Embedded Database for Python

PostgreSQL Indexing: Choosing Between B-Tree, GIN, and BRIN

PostgreSQL Indexing: Choosing Between B-Tree, GIN, and BRIN

Core Web Vitals Optimization in Next.js App Router

Core Web Vitals Optimization in Next.js App Router