When you trust third parties with your password vault, there’s always some risk. Most password manager services use zero-knowledge encryption to keep your passwords safe, but they still have enough information about you to identify you, and there may be useful metadata or billing information stored. Furthermore, if the service is offline for maintenance or under a denial-of-service attack, you may have trouble accessing your passwords. All password manager services cost money if you want all the features. The free plans provide only basic functionality for a single user.
Hosting your own password vault comes with plenty of risks, too. You are solely responsible for patches, maintenance, backups, and providing support to family, co-workers or friends using your solution. I cannot overstate the seriousness and responsibility that comes with managing a service like this for other people, or the consequences of doing it poorly.
BitWarden is open-source, and there are several alternative implementations that work well. For ease of use, I’ll focus on deploying VaultWarden in Docker with an nginx front-end. My production environment is running the rust server directly on OpenBSD with relayd.
You’ll need a DNS domain, access to create DNS entries, and a public-facing server that’s accessible via HTTPS. You might be able to run this at home, but a dedicated server, VPS or cloud server might be more reliable. I’ll be using the latest Debian bookworm for this, but Ubuntu Server, Amazon Linux, etc should work fine with minor adjustments.
You should have a DNS entry for your VaultWarden server. The examples use vault.yourdomain.com and you should adjust that accordingly.
Make sure all the packages are up to date
sudo apt update && sudo apt upgrade -y
Install packages:
sudo apt install -y docker.io nginx docker-compose certbot
Add yourself to the docker group
sudo usermod -aG docker ${USER}
newgrp docker
Create a directory for VaultWarden
mkdir vaultwarden
cd vaultwarden
This will download the latest VaultWarden docker image and then run a command inside a temporary container to generate the strong encrypted hash for the administrator login page.
docker run --rm -it vaultwarden/server /vaultwarden hash
Create a strong password.
Create .env file based on the output of the hash generator. There are many special characters in the hash, and it's easiest to just reference this value as a variable rather than trying to get it into the docker-compose.yml file directly. Note the prefix “VAULTWARDEN_” on this line.
VAULTWARDEN_ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4…'
Create docker-compose.yml:
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
volumes:
- ./vw-data:/data
- /var/log/vaultwarden:/var/log/vaultwarden
environment:
- DOMAIN=https://vault.yourdomain.com
- SIGNUPS_ALLOWED=true # Set to false after creating accounts
- SHOW_PASSWORD_HINT=false
- ROCKET_PORT=80
- LOG_FILE=/var/log/vaultwarden/vaultwarden.log
- LOG_LEVEL=info
- EXTENDED_LOGGING=true
- IP_HEADER=X-Forwarded-For
- ADMIN_TOKEN=${VAULTWARDEN_ADMIN_TOKEN}
ports:
- "127.0.0.1:8080:80"
We have a chicken-and-egg problem now. nginx will be running after installation, but we can't configure it for TLS without the certificates from LetsEncrypt, and we can't run certbot while nginx is running. Let's stop nginx and run certbot manually. Don't forget to use your correct DNS name and email address for the certbot command.
sudo systemctl stop nginx
sudo certbot certonly --standalone -d vault.yourdomain.com --non-interactive --agree-tos --email you@email.com --redirect
Create the nginx config file /etc/nginx/sites-available/vault
# Redirect HTTP to HTTPS
server {
listen 80;
listen [::]:80;
server_name vault.yourdomain.com;
return 301 https://$server_name$request_uri;
}
# HTTPS Server
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name vault.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/vault.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vault.yourdomain.com/privkey.pem;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "same-origin" always;
# Proxy to Vaultwarden
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 support for live sync
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
# Increase file size for vault exports/imports & sends
client_max_body_size 128M;
}
# WebSocket connection upgrade map
map $http_upgrade $connection_upgrade {
default upgrade;
'' "";
}
Enable this site by creating a symlink to it from sites-enabled:
sudo ln -s /etc/nginx/sites-available/vault /etc/nginx/sites-enabled/vault
Use this fun trick to replace all the host names in your docker-compose and nginx files, if you didn't edit them manually already. Obviously, replace "vault.h-i-r.net" with your own host name.
sudo sed -i 's/vault.yourdomain.com/vault.h-i-r.net/g' /etc/nginx/sites-available/vault ~/vaultwarden/docker-compose.yml
Start Vaultwarden and check the logs:
cd ~/vaultwarden
docker-compose up -d
docker-compose logs
Test the nginx configuration first:
sudo nginx -t
If no errors are found, start nginx:
sudo systemctl start nginx
Browse to your vault URL and create an account. Choose a strong master password. I recommend a passphrase. Resist the urge to use your web browser’s built-in password manager to generate a password, and do not allow the web browser to store this password.
You can import passwords, generate new ones, set up multi-factor authentication, set up shared passwords and more.
Visit the VaultWarden admin panel at your URL /admin
Log in using the password you used to generate the admin token. Here, you can tune configuration options, manage user accounts, disable new user signups, set a new admin token, etc. Generally, the default values are safe and most of the premium paid features are enabled by default.
Install the official BitWarden browser plugins, desktop and mobile apps for your platforms. When you first attempt to login, select the “self-hosted” server and enter the URL for your new VaultWarden server. Sign in with the username and master password you created. Once installed, you can also set up biometric and passkey unlock.
To back up your own personal vault, use web or desktop, and export an encrypted, password-protected json backup. This file is resistant to cracking, and is good enough to keep saved on unencrypted backups, an external drive you keep in a safe place, or even cloud storage, so long as the password is very strong.