Vaultwarden
Purpose: Self-hosted password manager compatible with Bitwarden
Category: Security & Collaboration
Access URL: https://vaultinas.billpantzartzis.info
Status: 🟢 Active
📋 Description
Vaultwarden is a lightweight, self-hosted password manager that implements the Bitwarden API. Written in Rust, it offers a resource-efficient alternative to the official Bitwarden server while maintaining full compatibility with Bitwarden clients. Perfect for individuals and small teams who want complete control over their password data.
Key Features
- ✅ Full Bitwarden Compatibility: Works with all official Bitwarden apps and browser extensions
- ✅ Low Resource Usage: Runs efficiently on minimal hardware (< 100MB RAM)
- ✅ WebSocket Support: Real-time sync across all devices
- ✅ 2FA Support: TOTP, U2F, YubiKey, and Duo authentication
- ✅ Organizations: Share passwords securely with teams
- ✅ File Attachments: Store secure notes and files
- ✅ Admin Panel: Built-in administration interface
- ✅ Send Feature: Securely share text and files with anyone
🏗️ Architecture
🚀 Deployment
- Docker Compose
- Docker CLI
- Portainer Stack
version: '3.8'
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
# Domain configuration
DOMAIN: "https://vault.example.com"
# Security settings
SIGNUPS_ALLOWED: "false"
INVITATIONS_ALLOWED: "true"
SHOW_PASSWORD_HINT: "false"
# Admin panel
ADMIN_TOKEN: "${ADMIN_TOKEN}" # Generate with: openssl rand -base64 48
# WebSocket
WEBSOCKET_ENABLED: "true"
# SMTP Configuration
SMTP_HOST: "smtp.gmail.com"
SMTP_PORT: 587
SMTP_SECURITY: "starttls"
SMTP_FROM: "vault@example.com"
SMTP_USERNAME: "${SMTP_USERNAME}"
SMTP_PASSWORD: "${SMTP_PASSWORD}"
# Database
DATABASE_URL: "data/db.sqlite3"
# Logging
LOG_LEVEL: "info"
EXTENDED_LOGGING: "true"
LOG_FILE: "/data/vaultwarden.log"
# Security headers
ROCKET_LIMITS: "{json=10485760}"
ROCKET_WORKERS: 10
volumes:
- ./vw-data:/data
- ./backups:/backups
ports:
- "3012:3012" # WebSocket port
- "8080:80" # HTTP port (use reverse proxy for HTTPS)
# Health check
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/alive"]
interval: 30s
timeout: 3s
retries: 3
start_period: 30s
# Optional: PostgreSQL for better performance
# postgres:
# image: postgres:15-alpine
# container_name: vaultwarden-db
# restart: unless-stopped
# environment:
# POSTGRES_DB: vaultwarden
# POSTGRES_USER: vaultwarden
# POSTGRES_PASSWORD: "${DB_PASSWORD}"
# volumes:
# - ./postgres-data:/var/lib/postgresql/data
# Backup service
backup:
image: bruceforce/vaultwarden-backup:latest
container_name: vaultwarden-backup
restart: unless-stopped
depends_on:
- vaultwarden
environment:
BACKUP_INTERVAL: 12h
BACKUP_KEEP_DAYS: 30
TIMESTAMP: "true"
volumes:
- ./vw-data:/data:ro
- ./backups:/backups
- /etc/localtime:/etc/localtime:ro
# Create data directory
mkdir -p /opt/vaultwarden/data
# Generate admin token
ADMIN_TOKEN=$(openssl rand -base64 48)
echo "Admin Token: $ADMIN_TOKEN"
# Run Vaultwarden container
docker run -d \
--name vaultwarden \
--restart unless-stopped \
-e DOMAIN="https://vault.example.com" \
-e SIGNUPS_ALLOWED="false" \
-e INVITATIONS_ALLOWED="true" \
-e ADMIN_TOKEN="$ADMIN_TOKEN" \
-e WEBSOCKET_ENABLED="true" \
-v /opt/vaultwarden/data:/data \
-p 8080:80 \
-p 3012:3012 \
vaultwarden/server:latest
Deploy via Portainer using the Docker Compose configuration above.
Stack Name: vaultwarden-stack
Environment Variables to Configure:
ADMIN_TOKEN=<generate-secure-token>
SMTP_USERNAME=your-email@gmail.com
SMTP_PASSWORD=your-app-password
DB_PASSWORD=secure-database-password
Volumes to Create:
- vaultwarden-data
- vaultwarden-backups
⚙️ Configuration
Environment Variables
Never commit sensitive environment variables to version control. Use .env files or secrets management.
📋 Complete Environment Variables List
| Variable | Description | Default | Required |
|---|---|---|---|
| Core Settings | |||
DOMAIN | Full URL for the service | - | ✅ |
ROCKET_PORT | Internal port | 80 | ❌ |
ROCKET_WORKERS | Number of worker threads | 10 | ❌ |
| Security | |||
ADMIN_TOKEN | Admin panel access token | - | ✅ |
SIGNUPS_ALLOWED | Allow new user registration | true | ❌ |
INVITATIONS_ALLOWED | Allow user invitations | true | ❌ |
SIGNUPS_VERIFY | Require email verification | false | ❌ |
SIGNUPS_DOMAINS_WHITELIST | Allowed email domains | - | ❌ |
SHOW_PASSWORD_HINT | Display password hints | true | ❌ |
| Database | |||
DATABASE_URL | Database connection string | SQLite | ❌ |
DATABASE_MAX_CONNS | Max database connections | 10 | ❌ |
SMTP_HOST | SMTP server address | - | ❌ |
SMTP_PORT | SMTP server port | 587 | ❌ |
SMTP_SECURITY | starttls, force_tls, or off | starttls | ❌ |
SMTP_FROM | From email address | - | ❌ |
SMTP_USERNAME | SMTP username | - | ❌ |
SMTP_PASSWORD | SMTP password | - | ❌ |
| Advanced | |||
WEBSOCKET_ENABLED | Enable WebSocket notifications | false | ❌ |
WEBSOCKET_PORT | WebSocket port | 3012 | ❌ |
LOG_LEVEL | trace, debug, info, warn, error | info | ❌ |
EXTENDED_LOGGING | Enable extended logging | true | ❌ |
LOG_FILE | Log file path | - | ❌ |
ICON_SERVICE | Icon download service | internal | ❌ |
ICON_CACHE_TTL | Icon cache time (seconds) | 2592000 | ❌ |
DISABLE_ICON_DOWNLOAD | Disable favicon downloads | false | ❌ |
Reverse Proxy Configuration
- Nginx
- Traefik
server {
listen 443 ssl http2;
server_name vault.example.com;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/vault.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vault.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" 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;
# Vaultwarden proxy
location / {
proxy_pass http://localhost: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
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# WebSocket specific location
location /notifications/hub {
proxy_pass http://localhost:3012;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
}
# Admin panel (optional: restrict by IP)
location /admin {
# allow 192.168.1.0/24;
# deny all;
proxy_pass http://localhost: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;
}
}
services:
vaultwarden:
labels:
- "traefik.enable=true"
# HTTP Router
- "traefik.http.routers.vaultwarden.rule=Host(`vault.example.com`)"
- "traefik.http.routers.vaultwarden.entrypoints=websecure"
- "traefik.http.routers.vaultwarden.tls=true"
- "traefik.http.routers.vaultwarden.tls.certresolver=letsencrypt"
- "traefik.http.routers.vaultwarden.service=vaultwarden"
- "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
# WebSocket Router
- "traefik.http.routers.vaultwarden-ws.rule=Host(`vault.example.com`) && Path(`/notifications/hub`)"
- "traefik.http.routers.vaultwarden-ws.entrypoints=websecure"
- "traefik.http.routers.vaultwarden-ws.tls=true"
- "traefik.http.routers.vaultwarden-ws.service=vaultwarden-ws"
- "traefik.http.services.vaultwarden-ws.loadbalancer.server.port=3012"
# Security Headers
- "traefik.http.middlewares.vaultwarden-headers.headers.stsSeconds=31536000"
- "traefik.http.middlewares.vaultwarden-headers.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.vaultwarden-headers.headers.stsPreload=true"
📖 Usage
Initial Setup
Follow these steps to configure Vaultwarden after deployment
-
Access the Web Vault
- Navigate to
https://vault.example.com - Click "Create Account"
- Enter your email and master password
- Navigate to
-
Configure Admin Panel
# Access admin panel at:
https://vault.example.com/admin
# Enter the admin token from your environment variables -
Recommended Admin Settings
- Disable open registration:
SIGNUPS_ALLOWED=false - Enable email verification:
SIGNUPS_VERIFY=true - Configure SMTP for email notifications
- Set backup retention policies
- Disable open registration:
-
Install Clients
Client Configuration
🔧 Configure Bitwarden Clients
- Open the Bitwarden app/extension
- Click the gear icon (Settings)
- Select "Self-hosted environment"
- Enter your server URL:
- Server URL:
https://vault.example.com - Leave other fields empty
- Server URL:
- Save and log in with your credentials
Common Tasks
📝 Backup and Restore
Automated Backups
#!/bin/bash
# Vaultwarden backup script
BACKUP_DIR="/backups"
DATA_DIR="/data"
DATE=$(date +%Y%m%d_%H%M%S)
# Stop Vaultwarden for consistency
docker stop vaultwarden
# Create backup
tar -czf "$BACKUP_DIR/vaultwarden_$DATE.tar.gz" \
-C "$DATA_DIR" .
# Start Vaultwarden
docker start vaultwarden
# Remove old backups (keep 30 days)
find "$BACKUP_DIR" -name "vaultwarden_*.tar.gz" \
-mtime +30 -delete
echo "Backup completed: vaultwarden_$DATE.tar.gz"
Restore from Backup
# Stop Vaultwarden
docker stop vaultwarden
# Backup current data (just in case)
mv /data /data.old
# Extract backup
tar -xzf /backups/vaultwarden_20240101_120000.tar.gz \
-C /data
# Start Vaultwarden
docker start vaultwarden
🔄 Update Vaultwarden
# Pull latest image
docker pull vaultwarden/server:latest
# Stop current container
docker stop vaultwarden
# Remove old container (data is preserved in volumes)
docker rm vaultwarden
# Start with new image
docker-compose up -d
# Verify update
docker logs vaultwarden | grep "Version"
👥 User Management
Invite New Users
- Access Admin Panel:
https://vault.example.com/admin - Navigate to "Users" section
- Click "Invite User"
- Enter email address
- User receives invitation email
Disable/Enable Users
-- Via SQLite (backup first!)
sqlite3 /data/db.sqlite3
-- Disable user
UPDATE users SET enabled = 0 WHERE email = 'user@example.com';
-- Enable user
UPDATE users SET enabled = 1 WHERE email = 'user@example.com';
-- List all users
SELECT email, enabled, created_at FROM users;
🔧 Troubleshooting
Common Issues
Most problems are related to reverse proxy or WebSocket configuration
❌ Cannot Access Web Vault
Symptoms: Browser shows connection error or timeout
Solutions:
-
Check container status:
docker ps | grep vaultwarden
docker logs vaultwarden -
Verify port mapping:
netstat -tulpn | grep 8080 -
Test direct access:
curl http://localhost:8080/alive -
Check reverse proxy:
- Verify SSL certificates
- Check proxy configuration
- Review proxy logs
🔄 Sync Not Working
Symptoms: Changes don't sync between devices
Solutions:
-
Enable WebSocket:
WEBSOCKET_ENABLED: "true" -
Configure reverse proxy for WebSocket:
- Ensure
/notifications/hubis proxied to port 3012 - Add WebSocket headers
- Ensure
-
Check client logs:
- Browser: F12 → Console
- Mobile: App settings → Sync status
-
Force sync:
- Pull down to refresh (mobile)
- Click sync button (desktop)
🔐 Forgot Admin Token
Recovery Steps:
-
Generate new token:
openssl rand -base64 48 -
Update container:
docker stop vaultwarden
docker rm vaultwarden
docker run -d \
--name vaultwarden \
-e ADMIN_TOKEN="new-token-here" \
[other options...] -
Alternative: Disable admin panel:
# Remove ADMIN_TOKEN environment variable
# Admin panel will be disabled
🔐 Security Best Practices
Hardening Checklist
Implement these measures for production deployments
- ✅ Use HTTPS only - Never expose over HTTP
- ✅ Strong admin token - Minimum 48 characters
- ✅ Disable signups - Use invitations only
- ✅ Enable 2FA - Require for all users
- ✅ Regular backups - Automated and tested
- ✅ Update regularly - Subscribe to security advisories
- ✅ Firewall rules - Restrict admin panel access
- ✅ Fail2ban - Protect against brute force
- ✅ Audit logs - Monitor access patterns
Fail2ban Configuration
🛡️ Fail2ban Setup
[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <HOST>\. Username:.*$
ignoreregex =
[vaultwarden]
enabled = true
port = 80,443,8080
filter = vaultwarden
logpath = /opt/vaultwarden/data/vaultwarden.log
maxretry = 5
bantime = 3600
findtime = 600
📊 Monitoring
Health Checks
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/alive"]
interval: 30s
timeout: 3s
retries: 3
Metrics & Logging
| Metric | Location | Purpose |
|---|---|---|
| Access logs | /data/vaultwarden.log | User activity |
| Error logs | Docker logs | Troubleshooting |
| Database size | /data/db.sqlite3 | Storage monitoring |
| Backup status | /backups/ | Verify backups |
Monitoring with Prometheus
scrape_configs:
- job_name: 'vaultwarden'
static_configs:
- targets: ['vaultwarden:80']
metrics_path: '/alive'
🔗 Resources
- 📚 Official Wiki
- 🐛 GitHub Issues
- 💬 Community Forum
- 🎥 Setup Tutorial
- 🔐 Security Hardening Guide
- 📱 Client Downloads
Last Updated: December 2024
Version: vaultwarden/server:latest (1.30.x)
Maintainer: Infrastructure Team
Backup Schedule: Daily at 2 AM