Self-Host Your Own Password Manager with Vaultwarden on a $5 VPS
Bitwarden is excellent software. The free tier is genuinely useful, and the premium plan runs about $10 a year, which is absurdly cheap for what you get. So why self-host at all? A few real reasons: you want full control over where your vault lives, your employer blocks third-party password managers, or you just enjoy owning your stack. All valid.
Vaultwarden is an unofficial, open-source Bitwarden server written in Rust. It's compatible with all official Bitwarden clients (browser extensions, mobile apps, desktop apps) and runs comfortably on a machine with 512MB of RAM. A $5/month Linode, Hetzner or DigitalOcean droplet handles it without breaking a sweat.
This guide walks you through a production-ready Vaultwarden setup: Docker, Nginx as a reverse proxy, and HTTPS via Let's Encrypt. Expect to spend about an hour from zero to a working vault.
What You'll Need
- A VPS running Ubuntu 22.04 (the tutorial assumes this, though 20.04 works fine)
- A domain name with an A record pointing to your VPS IP
- Basic comfort with the Linux command line
- Docker and Docker Compose installed on the server
If you don't have Docker yet, the official install script is the fastest path:
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
Log out and back in after running that so the group change takes effect.
Setting Up the Directory Structure
Create a working directory for your Vaultwarden config:
mkdir -p ~/vaultwarden && cd ~/vaultwarden
You'll store your docker-compose.yml here along with a directory for persistent data.
mkdir -p ./vw-data
Writing the Docker Compose File
Create docker-compose.yml with the following content:
version: '3.8'
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: "https://vault.yourdomain.com"
SIGNUPS_ALLOWED: "false"
ADMIN_TOKEN: "your-very-long-random-token-here"
volumes:
- ./vw-data:/data
ports:
- "127.0.0.1:8080:80"
A few things worth calling out here. SIGNUPS_ALLOWED: "false" is important once you've created your account. Without it, anyone who finds your instance URL can register. Set ADMIN_TOKEN to a long random string, which you can generate with openssl rand -base64 48. The port binding to 127.0.0.1:8080 means Vaultwarden only accepts connections from localhost, so Nginx handles all external traffic.
Start the container:
docker compose up -d
Configuring Nginx as a Reverse Proxy
Install Nginx if it's not already present:
sudo apt update && sudo apt install -y nginx
Create a new site config at /etc/nginx/sites-available/vaultwarden:
server {
listen 80;
server_name vault.yourdomain.com;
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;
}
}
Enable it and test:
sudo ln -s /etc/nginx/sites-available/vaultwarden /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Adding HTTPS with Certbot
Vaultwarden requires HTTPS to function properly. The Bitwarden clients won't connect to a plain HTTP endpoint, which is a sensible constraint.
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d vault.yourdomain.com
Certbot will modify your Nginx config automatically and set up auto-renewal. After this step, your Nginx config will handle the SSL termination and Vaultwarden will receive plain HTTP traffic from localhost.
Verify the renewal timer is active:
sudo systemctl status certbot.timer
Creating Your Account
Navigate to https://vault.yourdomain.com in your browser. You'll see the standard Bitwarden web interface. Create your account here. Once you've done that, flip SIGNUPS_ALLOWED to false in your docker-compose.yml and restart the container:
docker compose down && docker compose up -d
You can also visit https://vault.yourdomain.com/admin using your ADMIN_TOKEN to manage users, check diagnostics, and configure things like SMTP for email-based two-factor authentication.
Connecting the Bitwarden Clients
This is where the payoff happens. In any official Bitwarden app, look for the option to set a custom server URL before logging in. On the browser extension, click the settings gear on the login screen. Point it to https://vault.yourdomain.com and log in with the account you just created.
All your existing Bitwarden data can be exported from bitwarden.com (Settings > Export Vault) and imported into your self-hosted instance through the web vault. The format is a standard JSON file.
Keeping It Maintained
Vaultwarden releases updates regularly. Since you're using the latest tag, pulling updates is straightforward:
docker compose pull && docker compose up -d
That said, pinning to a specific version tag (like vaultwarden/server:1.30.5) gives you more control over when updates happen, which matters for a service storing sensitive credentials. Check the Vaultwarden GitHub releases page before upgrading.
Also set up automated backups of your ./vw-data directory. A simple cron job that tarballs it and pushes to Backblaze B2 or even just another VPS takes maybe 20 minutes to configure and will save you someday.
# Example backup cron (runs daily at 3am)
0 3 * * * tar -czf /root/vw-backup-$(date +\%F).tar.gz /root/vaultwarden/vw-data
Firewall Basics
If you haven't already, enable UFW and lock down your VPS:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
This permits SSH, HTTP, and HTTPS while blocking everything else, including direct access to port 8080.
Closing Thoughts
Running your own Vaultwarden instance is one of the more practical self-hosting projects out there because you'll actually use it every day. The maintenance burden is low, the hardware requirements are minimal, and you end up with full ownership of credential data that really does matter. On a $5/month VPS, the total cost comes in well under the Bitwarden premium plan anyway, with the added upside of running whatever other services you want on the same machine.