3.8 KiB
Vaultwarden Deployment (rayserver)
Complete worked example for deploying Vaultwarden (Bitwarden-compatible password manager) on rayserver infrastructure.
Architecture
Browser → VPS (vault.graj-media.com:443, nginx SSL)
↳ Tailscale → Home server (100.93.253.36:3446, nginx SSL)
↳ Vaultwarden (127.0.0.1:8812, Docker)
Docker Compose
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
volumes:
- /mnt/seagate8tb/docker/vaultwarden:/data
ports:
- "127.0.0.1:8812:80"
environment:
- DOMAIN=https://vault.graj-media.com
- SIGNUPS_ALLOWED=false
- WEBSOCKET_ENABLED=true
- LOG_FILE=/data/vaultwarden.log
- LOG_LEVEL=warn
Key settings:
DOMAIN— must match the public URL for correct invite links and CORSSIGNUPS_ALLOWED— enable temporarily for account creation, disable afterWEBSOCKET_ENABLED— required for live sync with browser extensions- Bind to
127.0.0.1— all access goes through nginx
Nginx WebSocket Configuration
Vaultwarden uses WebSocket for the notifications hub. The nginx config needs a dedicated location block for /notifications/hub with Upgrade and Connection headers, plus a separate block for the negotiate endpoint:
server {
listen 3446 ssl;
server_name vault.graj-media.com;
ssl_certificate /etc/letsencrypt/live/grajmedia.duckdns.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/grajmedia.duckdns.org/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
client_max_body_size 128M;
location / {
proxy_pass http://127.0.0.1:8812;
proxy_http_version 1.1;
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;
}
location /notifications/hub {
proxy_pass http://127.0.0.1:8812;
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;
}
location /notifications/hub/negotiate {
proxy_pass http://127.0.0.1:8812;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
}
Without the WebSocket blocks, browser extensions can't maintain a live sync connection — they'll work for manual syncs but won't push changes in real-time.
Admin Token Setup
After first deployment, set the admin token via Docker environment variable. The token MUST be hashed with argon2id:
# Generate random token
ADMIN_TOKEN=$(openssl rand -hex 32)
# Hash with argon2id using the Vaultwarden container
HASHED=$(echo -n "$ADMIN_TOKEN" | sudo docker exec -i vaultwarden /vaultwarden hash)
# Add to docker-compose.yml environment section
# - ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$...'
# Then restart: sudo docker compose up -d
Access admin panel at https://vault.graj-media.com/admin — use the raw (unhashed) token to log in.
Post-Deploy Steps
- Open signups temporarily: set
SIGNUPS_ALLOWED=true, restart container - Create user account: visit https://vault.graj-media.com, register
- Disable signups: set
SIGNUPS_ALLOWED=false, restart container - Configure Bitwarden apps: Settings → Self-hosted → Server URL:
https://vault.graj-media.com
Bitwarden Client Setup
All official Bitwarden clients support self-hosted servers:
- Browser extension: Settings → Self-Hosted Environment → Server URL
- Desktop app: Settings → Self-Hosted Environment
- Mobile (iOS/Android): Settings → Self-Hosted Environment
Enter https://vault.graj-media.com as the server URL. No trailing slash.