Install Vaultwarden on Ubuntu Server
Why Ubuntu for Vaultwarden?
Ubuntu Server is the most common deployment target for self-hosted services, and Vaultwarden runs perfectly on it. This guide covers the full production setup: Docker deployment, mandatory HTTPS via a reverse proxy with Let’s Encrypt, UFW firewall lockdown, fail2ban for brute-force protection, and automated SQLite backups via cron. If you want a password vault you can actually trust in production, this is the guide.
Updated March 2026: Verified with latest Docker images and configurations.
Prerequisites
- Ubuntu 22.04 or 24.04 LTS server (fresh or existing)
- Docker and Docker Compose installed (guide)
- A domain name pointed at your server’s public IP (A record)
- 256 MB of free RAM
- 500 MB of free disk space
- SSH access with a sudo-capable user
- Ports 80 and 443 open on your network (for Let’s Encrypt and HTTPS)
Install Docker
If Docker is not already installed:
sudo apt update && sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker $USER
Log out and back in for the group change to take effect.
Configure UFW Firewall
Lock down the server before deploying anything:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Docker bypasses UFW by default because it manipulates iptables directly. To fix this, edit /etc/docker/daemon.json:
{
"iptables": false
}
Then restart Docker:
sudo systemctl restart docker
With "iptables": false, Docker will no longer punch holes through UFW. You will need to manage port exposure carefully in your Compose files. Since we are running Vaultwarden behind a reverse proxy on the same Docker network, no host port mapping is needed for Vaultwarden itself.
Docker Compose Configuration
Create your project directory:
mkdir -p ~/vaultwarden && cd ~/vaultwarden
Create a docker-compose.yml:
services:
vaultwarden:
image: vaultwarden/server:1.35.4
container_name: vaultwarden
restart: unless-stopped
environment:
# REQUIRED: Set your domain (must be HTTPS)
- DOMAIN=https://vault.example.com
# Admin panel — generate a token with: openssl rand -base64 48
- ADMIN_TOKEN=${ADMIN_TOKEN}
# Disable new user signups after you create your account
- SIGNUPS_ALLOWED=true
# Enable WebSocket notifications for browser extension sync
- WEBSOCKET_ENABLED=true
# Rate limiting for login attempts
- LOGIN_RATELIMIT_MAX_BURST=5
- LOGIN_RATELIMIT_SECONDS=60
# Log to stdout
- LOG_FILE=/data/vaultwarden.log
- LOG_LEVEL=warn
- EXTENDED_LOGGING=true
volumes:
- vaultwarden-data:/data
networks:
- proxy
# No port mapping — reverse proxy handles external access
nginx-proxy:
image: jc21/nginx-proxy-manager:2.14.0
container_name: nginx-proxy-manager
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "81:81" # Admin panel — restrict access after setup
volumes:
- npm-data:/data
- npm-letsencrypt:/etc/letsencrypt
networks:
- proxy
volumes:
vaultwarden-data:
npm-data:
npm-letsencrypt:
networks:
proxy:
name: proxy
Create a .env file:
# Generate a secure admin token
ADMIN_TOKEN=$(openssl rand -base64 48)
Generate your actual .env:
echo "ADMIN_TOKEN=$(openssl rand -base64 48)" > .env
Start the stack:
docker compose up -d
Configure the Reverse Proxy
Bitwarden clients refuse to connect over plain HTTP. HTTPS is mandatory.
- Open Nginx Proxy Manager at
http://your-server-ip:81 - Default login:
[email protected]/changeme(change immediately) - Add a Proxy Host:
- Domain:
vault.example.com - Scheme:
http - Forward Hostname:
vaultwarden - Forward Port:
8080 - WebSocket Support: enabled
- Domain:
- Under the SSL tab:
- Select “Request a new SSL Certificate”
- Enable “Force SSL”
- Enable “HTTP/2 Support”
- Agree to Let’s Encrypt ToS
- Save
Test by navigating to https://vault.example.com. You should see the Vaultwarden web vault.
First-Time Setup
- Go to
https://vault.example.comand create your account - After creating your account, disable signups by editing your
.env:
# In docker-compose.yml, change:
# SIGNUPS_ALLOWED=true -> SIGNUPS_ALLOWED=false
Then restart:
docker compose up -d
- Access the admin panel at
https://vault.example.com/adminusing the token from your.envfile - Install a Bitwarden client (browser extension, desktop, or mobile) and connect it to
https://vault.example.com
Set Up fail2ban
Protect against brute-force login attempts. Vaultwarden logs failed logins to its log file, and fail2ban watches for patterns.
Install fail2ban:
sudo apt install -y fail2ban
Create a filter at /etc/fail2ban/filter.d/vaultwarden.conf:
[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
ignoreregex =
Create a jail at /etc/fail2ban/jail.d/vaultwarden.local:
[vaultwarden]
enabled = true
port = 80,443
filter = vaultwarden
# Path to the Vaultwarden log file inside the named volume
logpath = /var/lib/docker/volumes/vaultwarden_vaultwarden-data/_data/vaultwarden.log
maxretry = 5
bantime = 3600
findtime = 600
action = iptables-allports[name=vaultwarden, chain=FORWARD]
Restart fail2ban:
sudo systemctl restart fail2ban
sudo fail2ban-client status vaultwarden
Automated Backups via Cron
Vaultwarden uses SQLite, so backups are straightforward. Create a backup script at ~/vaultwarden/backup.sh:
#!/bin/bash
BACKUP_DIR="$HOME/vaultwarden-backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
DATA_DIR="/var/lib/docker/volumes/vaultwarden_vaultwarden-data/_data"
mkdir -p "$BACKUP_DIR"
# Use sqlite3 .backup for a safe, consistent copy
sqlite3 "$DATA_DIR/db.sqlite3" ".backup '$BACKUP_DIR/db_$TIMESTAMP.sqlite3'"
# Copy attachments and other data files
cp -r "$DATA_DIR/attachments" "$BACKUP_DIR/attachments_$TIMESTAMP" 2>/dev/null
cp "$DATA_DIR/rsa_key.pem" "$BACKUP_DIR/rsa_key_$TIMESTAMP.pem" 2>/dev/null
cp "$DATA_DIR/rsa_key.pub.pem" "$BACKUP_DIR/rsa_key_pub_$TIMESTAMP.pem" 2>/dev/null
# Remove backups older than 30 days
find "$BACKUP_DIR" -type f -mtime +30 -delete
find "$BACKUP_DIR" -type d -empty -mtime +30 -delete
echo "Backup completed: $TIMESTAMP"
Make it executable and add to cron:
chmod +x ~/vaultwarden/backup.sh
crontab -e
Add this line for daily backups at 3 AM:
0 3 * * * /home/$USER/vaultwarden/backup.sh >> /home/$USER/vaultwarden/backup.log 2>&1
Ubuntu-Specific Optimization
Automatic security updates. Enable unattended-upgrades to keep Ubuntu patched:
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
Swap space. If your server has limited RAM, add a swap file:
sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Docker log rotation. Prevent Docker logs from filling your disk. Add to /etc/docker/daemon.json:
{
"iptables": false,
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Troubleshooting
Bitwarden client says “connection refused” or “not secure”
HTTPS is mandatory. Bitwarden clients will not authenticate over plain HTTP. Verify your reverse proxy is working and the SSL certificate is valid:
curl -I https://vault.example.com
Admin panel returns 404
Make sure you have set ADMIN_TOKEN in your environment. Without a token, the admin panel is disabled entirely.
fail2ban is not banning IPs
Check that the log path in the jail config matches the actual Docker volume path:
sudo docker volume inspect vaultwarden_vaultwarden-data | grep Mountpoint
Also verify Vaultwarden is actually writing to its log file (check LOG_FILE env var).
WebSocket notifications not working
Ensure your reverse proxy has WebSocket support enabled. In Nginx Proxy Manager, the “WebSocket Support” toggle must be on for the Vaultwarden proxy host.
Container won’t start after upgrade
Check for breaking changes in the Vaultwarden release notes. Back up your data volume before upgrading image tags.
Resource Requirements
- RAM: ~50 MB idle, ~80 MB under active use
- CPU: Negligible. Vaultwarden is written in Rust and extremely efficient.
- Disk: ~50 MB for the application, plus your vault data (typically under 100 MB even with thousands of entries)
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments