Reverse Proxy

Running Valvet behind nginx or Caddy

When exposing Valvet to the internet, use a reverse proxy for TLS termination and additional security.

Caddy

Caddy is the simplest option — automatic HTTPS with Let’s Encrypt:

valvet.example.com {
    reverse_proxy localhost:4865
}

Caddy automatically handles WebSocket upgrade headers.

Nginx

server {
    listen 443 ssl http2;
    server_name valvet.example.com;

    ssl_certificate /etc/letsencrypt/live/valvet.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/valvet.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:4865;
        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";

        # SSE support — disable buffering
        proxy_buffering off;
        proxy_cache off;

        # Increase timeouts for long-running connections
        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;
    }
}

Important Notes

WebSocket Headers

The hub requires WebSocket connections from nodes. Your reverse proxy must forward the Upgrade and Connection headers. Without this, nodes cannot connect.

SSE Buffering

Server-Sent Events require that the proxy does not buffer responses. In nginx, set proxy_buffering off for the SSE endpoint. Caddy handles this automatically.

Timeouts

Node WebSocket connections are long-lived (hours to days). Set read/send timeouts high enough that the proxy does not close idle connections. The 30-second ping/pong keepalive keeps the connection active.

TLS for Nodes

If the hub is behind TLS, node enrollment and runtime connections must use https:// URLs:

valvet-node enroll --hub https://valvet.example.com --token <token>