Files
2026-07-12 10:17:17 -04:00

114 lines
3.8 KiB
Markdown

# 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
```yaml
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 CORS
- `SIGNUPS_ALLOWED` — enable temporarily for account creation, disable after
- `WEBSOCKET_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:
```nginx
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:
```bash
# 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
1. **Open signups temporarily**: set `SIGNUPS_ALLOWED=true`, restart container
2. **Create user account**: visit https://vault.graj-media.com, register
3. **Disable signups**: set `SIGNUPS_ALLOWED=false`, restart container
4. **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.