Vaultwarden stores highly sensitive credentials β passwords, API keys, secure notes, and authentication tokens. This guide provides security hardening for production deployments on Linux servers.
Security First: Treat your Vaultwarden instance as critical security infrastructure. Apply defense-in-depth principles and follow the principle of least privilege.
Current Version: 1.35.4 (February 2026)
| Layer | Technology | Description |
|---|---|---|
| Client-Side | AES-256-CBC | All data encrypted before leaving client |
| Key Derivation | PBKDF2-SHA256 / Argon2id | Password-to-key derivation |
| Transport | TLS 1.3 | HTTPS for all communications |
| Server-Side | Optional disk encryption | Database/file encryption at rest |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β UNTRUSTED NETWORK β
β ββββββββββββββββ β
β β Browser β βββββββββββββββββββββββββββββββββββ β
β β Mobile App β ββββββββββββββββββββββββββββ β β
β β Desktop β ββββββββββββββββββββ β β β
β ββββββββββββββββ β β β β
βββββββββββββββββββββββββββββββββββββββΌββββββββΌβββββββΌβββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TRUSTED ZONE β
β βββββββββββββββ ββββββββββββββββ ββββββββββββββββββββ β
β β Reverse β β Vaultwarden β β Database β β
β β Proxy βββΆβ Server βββΆβ (SQLite/PG) β β
β β (Nginx) β β (Docker) β β /data volume β β
β βββββββββββββββ ββββββββββββββββ ββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββ β
β β TLS β β
β β Certificatesβ β
β βββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Prevent unauthorized account creation:
# .env or docker-compose.yml
SIGNUPS_ALLOWED=false
Alternative: Allow only specific email domains:
SIGNUPS_ALLOWED=true
SIGNUPS_DOMAINS_WHITELIST=example.com,corp.example.com
SIGNUPS_VERIFY=true
The admin panel provides powerful administrative functions. Protect it rigorously:
# Generate secure admin token (32+ characters)
openssl rand -base64 48
# Use hashed token for additional security
ADMIN_TOKEN_HASH=<argon2id-hash>
Best Practices:
DISABLE_ADMIN_TOKEN=trueNginx Example β Restrict Admin Panel:
location /admin {
allow 10.0.0.0/8;
allow 192.168.0.0/16;
allow 172.16.0.0/12;
deny all;
proxy_pass http://127.0.0.1:8000;
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;
}
Never expose Vaultwarden without TLS. Use Letβs Encrypt or enterprise CA:
Nginx Configuration with TLS 1.3:
server {
listen 443 ssl http2;
server_name vault.example.com;
# TLS 1.3 only
ssl_protocols TLSv1.3;
ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256';
ssl_prefer_server_ciphers off;
# Certificate paths
ssl_certificate /etc/letsencrypt/live/vault.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vault.example.com/privkey.pem;
# 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;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' wss://$host;" always;
# Proxy settings
location / {
proxy_pass http://127.0.0.1:8000;
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";
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# WebSocket endpoint
location /notifications/hub {
proxy_pass http://127.0.0.1:3012;
proxy_http_version 1.1;
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;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name vault.example.com;
return 301 https://$server_name$request_uri;
}
# Force secure cookies (HTTPS only)
COOKIE_SECURE=true
# SameSite protection
COOKIE_SAME_SITE=Lax
services:
vaultwarden:
image: ghcr.io/dani-garcia/vaultwarden:1.35.3
container_name: vaultwarden
restart: unless-stopped
user: "1000:1000" # Run as non-root
volumes:
- ./vw-data:/data:Z
environment:
- DOMAIN=https://vault.example.com
- SIGNUPS_ALLOWED=false
- ADMIN_TOKEN=${ADMIN_TOKEN}
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=100m
networks:
- vaultwarden_net
networks:
vaultwarden_net:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/24
# Set restrictive permissions on data directory
sudo chown -R 1000:1000 /opt/vaultwarden/vw-data
sudo chmod -R 750 /opt/vaultwarden/vw-data
# Protect .env file
chmod 600 /opt/vaultwarden/.env
| Setting | Purpose | Value |
|---|---|---|
user |
Run as non-root | 1000:1000 |
security_opt |
Prevent privilege escalation | no-new-privileges:true |
cap_drop |
Drop all capabilities | ALL |
read_only |
Read-only root filesystem | true |
tmpfs |
Writable temp directory | /tmp:noexec,nosuid |
# Login rate limiting
LOGIN_RATELIMIT_MAX_BURST=5
LOGIN_RATELIMIT_SECONDS=120
# Admin panel rate limiting
ADMIN_RATELIMIT_MAX_BURST=3
ADMIN_RATELIMIT_SECONDS=300
# IP detection (behind reverse proxy)
IP_HEADER=X-Forwarded-For
Create /etc/fail2ban/jail.d/vaultwarden.local:
[vaultwarden]
enabled = true
port = 80,443
filter = vaultwarden
logpath = /opt/vaultwarden/vw-data/vaultwarden.log
maxretry = 5
bantime = 3600
findtime = 600
Create /etc/fail2ban/filter.d/vaultwarden.local:
[Definition]
failregex = ^.*Failed login attempt for user .+ from IP <HOST>.*$
^.*Invalid user or password.*from IP <HOST>.*$
^.*Invalid two-factor code.*from IP <HOST>.*$
ignoreregex =
Enable and start fail2ban:
sudo systemctl enable fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status vaultwarden
# Define rate limit zone
limit_req_zone $binary_remote_addr zone=vaultwarden_limit:10m rate=10r/s;
server {
location / {
limit_req zone=vaultwarden_limit burst=20 nodelay;
proxy_pass http://127.0.0.1:8000;
# ... other proxy settings
}
}
For SQLite deployments:
# Protect database file
chmod 640 /opt/vaultwarden/vw-data/db.sqlite3
chown 1000:1000 /opt/vaultwarden/vw-data/db.sqlite3
# Enable WAL mode for better concurrency
sqlite3 /opt/vaultwarden/vw-data/db.sqlite3 "PRAGMA journal_mode=WAL;"
For larger deployments using PostgreSQL:
# Create dedicated user with minimal privileges
sudo -u postgres psql <<EOF
CREATE USER vaultwarden WITH PASSWORD '<secure-password>';
CREATE DATABASE vaultwarden OWNER vaultwarden;
GRANT ALL PRIVILEGES ON DATABASE vaultwarden TO vaultwarden;
EOF
PostgreSQL Connection String:
DATABASE_URL=postgresql://vaultwarden:<secure-password>@localhost:5432/vaultwarden?sslmode=require
# Enable organization event logging
EVENTS_ENABLED=true
EVENTS_DAYS_RETAIN=90
# Production logging settings
LOG_LEVEL=info
LOG_FILE=/data/vaultwarden.log
LOG_TO_FILE=true
EXTENDED_LOGGING=false
| Event Type | Severity | Response |
|---|---|---|
| Failed login attempts | Medium | Alert on threshold breach |
| Admin panel access | High | Log and alert |
| New user registration | Medium | Review if signups disabled |
| Password export | High | Audit trail review |
| Organization changes | High | Immediate review |
| API key creation | High | Verify authorization |
# Forward logs to syslog
journalctl -u vaultwarden -f | logger -t vaultwarden
#!/bin/bash
# encrypted-backup.sh
BACKUP_DIR="/backup/vaultwarden"
DATE=$(date +%Y%m%d_%H%M%S)
DATA_DIR="/opt/vaultwarden/vw-data"
ENCRYPTION_KEY="/root/backup-key.gpg"
# Create and encrypt backup
tar -czf - -C "${DATA_DIR}" . | \
gpg --encrypt --recipient "backup@example.com" \
--output "${BACKUP_DIR}/vaultwarden_${DATE}.tar.gz.gpg"
# Securely delete unencrypted data
shred -u "${BACKUP_DIR}/vaultwarden_${DATE}.tar.gz" 2>/dev/null || true
Watch for security announcements:
# Check current version
docker exec vaultwarden vaultwarden --version
# Update Docker container
docker compose pull
docker compose up -d
# Verify update
docker logs vaultwarden | grep "Starting Vaultwarden"
Starting with v1.35.0, releases use immutable releases with attestation:
# Verify release signature (if cosign installed)
cosign verify \
--certificate-identity-regexp "https://github.com/dani-garcia/vaultwarden" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/dani-garcia/vaultwarden:1.35.3
Any questions?
Feel free to contact us. Find all contact information on our contact page.