Vaultwarden is the answer to a specific wish: you want a self-hosted password vault, you like the Bitwarden client apps, but you do not want to run Bitwarden’s full multi-container stack. Vaultwarden is an independent, Rust-based server that speaks the Bitwarden client API, so the official apps and extensions work against it while you run a single lightweight container.
This guide walks through a real, sane deployment, not a copy-paste-and-hope one. It is aimed at IT pros and homelab operators who are comfortable with Docker and a reverse proxy. If that is not you, the honest move is to use Bitwarden’s hosted service instead, and we will come back to when that is the right call.
Short verdict:
- self-host Vaultwarden if you are a small team or homelab operator who wants a lightweight, low-resource vault you fully control, and you are comfortable owning HTTPS, backups, and updates.
- do not self-host it if nobody will own the operations, or if you need enterprise features and vendor support.
Before you start: prerequisites and warnings
Vaultwarden is well engineered, but it is a community project, and it is refreshingly upfront about what that means. The maintainers state plainly that the project is “not associated with Bitwarden or Bitwarden, Inc.”, that they “cannot be held liable for any data loss” that may occur, and that you should keep regular backups. Source: Vaultwarden on GitHub. Read that as the operating contract: you are the support desk and the recovery plan.
You will need a host with Docker, a domain name pointing at it, and the ability to terminate HTTPS. That last point is not optional, for a reason worth understanding before you fight it: the Bitwarden web vault relies on the Web Crypto API, which browsers only expose in a secure context. Without HTTPS, the client-side cryptography will not run and the app simply will not work. Vaultwarden serves plain HTTP internally and expects a reverse proxy to handle TLS in front of it.
Deploying with Docker
The fastest correct start is the official image, vaultwarden/server. Pin a version tag in production rather than chasing latest, so an unattended pull cannot surprise you. A minimal run looks like this:
docker run --detach --name vaultwarden \
--env DOMAIN="https://vault.example.com" \
--volume /vw-data/:/data/ \
--restart unless-stopped \
--publish 127.0.0.1:8000:80 \
vaultwarden/server:latest
Note the publish binding to 127.0.0.1: the container listens only on localhost, and your reverse proxy connects to it locally. Nothing is exposed to the internet directly except through the proxy. Source: Vaultwarden on GitHub.
For anything you intend to keep, prefer Compose so the configuration lives in a file you can version and back up:
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: "https://vault.example.com"
SIGNUPS_ALLOWED: "true"
volumes:
- ./vw-data:/data
ports:
- 127.0.0.1:8000:80
By default Vaultwarden uses SQLite, which needs zero configuration and is the right choice for a small team. MariaDB, MySQL, and PostgreSQL are supported through a DATABASE_URL if you have a reason to use them, but most self-hosters do not. Source: Vaultwarden Docker Compose wiki.
Once it is running, create your own account immediately, then set SIGNUPS_ALLOWED to false and recreate the container. From then on you invite users rather than leaving open registration, which is one of the most common ways a personal instance ends up with strangers on it.
The admin panel and ADMIN_TOKEN
Vaultwarden has an admin panel for managing users and settings, enabled by setting an ADMIN_TOKEN. Do not store that token in plaintext. The project recommends storing an Argon2 hash of it instead, so the raw secret is not readable from your config or compose file. Generate one with the built-in command:
docker run --rm -it vaultwarden/server /vaultwarden hash
One sharp edge to know about: if you put the resulting Argon2 hash directly into a docker-compose.yml environment: block, you must double every $ to $$, because Compose performs variable substitution on the value. In a .env file, single-quote the value and no escaping is needed. Source: Vaultwarden admin page wiki. Getting this wrong is a classic “why does my admin login fail” afternoon.
Reverse proxy and TLS
This is the part you cannot skip. Put a reverse proxy in front of Vaultwarden to terminate HTTPS and forward to the container on localhost. Caddy, Nginx, and Traefik all work and are documented in the Vaultwarden wiki; Caddy is the easiest because it obtains and renews certificates automatically.
A complete Caddy site config can be as short as this:
vault.example.com {
reverse_proxy 127.0.0.1:8000
}
Caddy fetches a Let’s Encrypt certificate on first request and renews it for you. With Nginx or Traefik you do the same job with more configuration and your own certificate management. Whichever you pick, confirm that the public URL matches the DOMAIN you set on the container, because mismatches break attachments and WebAuthn.
Backups and updates
Everything Vaultwarden cares about lives in the /data directory, but “back up the folder” is not quite enough detail to be safe. The pieces that matter:
- The database. With SQLite, use the built-in
/vaultwarden backupcommand rather than copyingdb.sqlite3while the server is running. If you use an external MySQL or PostgreSQL backend, back up that database separately with its own dump; a filesystem copy will not capture it. attachments/andsends/. These are stored on disk, not in the database, and are easy to forget. Without them your restored vault is missing files.rsa_key.pemand friends. These sign authentication tokens. Losing them just logs everyone out, but a leaked private key would let someone forge sessions, so encrypt your off-site backups.config.json. Holds admin and SMTP settings including the admin token, so treat it as a secret and encrypt it before storing it anywhere remote.
Source for the backup specifics: Vaultwarden backup wiki. Test a restore at least once. A backup you have never restored is a hope, not a plan.
Updates are deliberately boring. Pull the new image and recreate the container, and your data persists in the volume:
docker compose pull && docker compose up -d
Pinning a version tag means you choose when to take an update and can read the release notes first, which is the grown-up way to run anything holding credentials.
Common mistakes
The failure modes here are predictable, which means they are avoidable:
- Running without HTTPS. The client crypto will not work in an insecure context. This is not optional.
- Leaving
SIGNUPS_ALLOWEDon. Create your account, then turn it off and invite the rest. - Forgetting the
$$escaping on the Argon2 admin token in Compose, then wondering why admin login fails. - Backing up only the database and losing
attachments/,sends/, or the RSA keys. - Chasing
latestand getting an unplanned breaking change instead of an update you scheduled.
Hardening beyond the basics
Once the core deployment is up, a few extra steps separate a hobby instance from one you would trust with a team’s credentials.
Lock down the admin panel rather than leaving it broadly reachable. After initial setup you can disable it entirely by unsetting ADMIN_TOKEN, and re-enable it only when you need it. If you keep it on, the Argon2-hashed token is the minimum bar, and restricting the /admin path at the reverse proxy to known IPs is a sensible addition.
Configure SMTP so you can invite users by email and send password-hint and verification messages; without it, onboarding new team members is clumsier than it needs to be. Put the instance behind the usual public-server hygiene as well: keep the host patched, restrict SSH, and consider fail2ban or your proxy’s rate limiting to blunt brute-force attempts against the login page. None of this is exotic, but it is the difference between “it works” and “it is safe to depend on”, and it costs an hour you will be glad you spent.
Finally, monitor it like the production service it now is. A vault that is quietly down when someone urgently needs a credential is its own kind of outage, so a basic uptime check and an alert on failed backups close the loop on running this responsibly.
When to stop and use Bitwarden instead
Vaultwarden is the right tool for a capable small operator. It is the wrong tool the moment “someone personally owns this server” stops being an acceptable answer.
If you need enterprise features such as SSO, SCIM, or directory sync, or you need a vendor relationship and real support for compliance and accountability, run official Bitwarden, hosted or self-hosted, rather than stretching a community container to fit. We laid out that exact trade-off in Bitwarden vs Vaultwarden, and put both in the wider context of team options in our hub on the best password managers for IT teams and MSPs in 2026.
Self-hosted right, Vaultwarden is a genuinely excellent vault that you own end to end. Self-hosted carelessly, it is a single point of failure for your most sensitive data. The difference is entirely in the operating discipline, which is the whole reason this guide spends more time on TLS and backups than on the install command.