Skip to content
🚧 These docs are a work in progress and may contain inaccuracies. Content is being actively reviewed and validated.

Reverse Proxy

Dubby listens on HTTP only and does not terminate TLS itself. A reverse proxy sits in front of Dubby to handle HTTPS, route traffic, and forward the real client IP for rate limiting and audit logs.

Dubby needs two headers from your proxy to identify clients correctly:

HeaderPurpose
X-Forwarded-ForClient IP for rate limiting
X-Real-IPClient IP fallback (Nginx-style)

Dubby also uses Server-Sent Events for real-time updates. Make sure your proxy does not buffer SSE responses and that its read timeout exceeds 30 seconds (Dubby’s default keepalive interval).

In the examples below, replace these values with your own:

PlaceholderExample
DOMAIN_NAMEdubby.example.com
SERVER_IP_ADDRESS192.168.1.100
DUBBY_PORT3000

Caddy is the simplest option — it handles HTTPS certificates automatically via Let’s Encrypt with no extra configuration.

dubby.example.com {
reverse_proxy SERVER_IP_ADDRESS:3000
}

That’s it. Caddy provisions a certificate, redirects HTTP to HTTPS, and sets the correct proxy headers by default.

For WebSocket-style keepalive on SSE connections, Caddy’s defaults work without changes.

services:
caddy:
image: caddy:2
restart: unless-stopped
ports:
- '80:80'
- '443:443'
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
volumes:
caddy-data:
caddy-config:
server {
listen 80;
server_name dubby.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name dubby.example.com;
ssl_certificate /etc/letsencrypt/live/dubby.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dubby.example.com/privkey.pem;
location / {
proxy_pass http://SERVER_IP_ADDRESS:3000;
# Standard proxy headers
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;
# SSE support — disable buffering and extend timeouts
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}

If Traefik manages your Docker containers via labels:

services:
dubby:
image: dubbytv/dubby:stable
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.dubby.rule=Host(`dubby.example.com`)'
- 'traefik.http.routers.dubby.entrypoints=websecure'
- 'traefik.http.routers.dubby.tls.certresolver=letsencrypt'
- 'traefik.http.services.dubby.loadbalancer.server.port=3000'
# Disable response buffering for SSE
- 'traefik.http.middlewares.dubby-buffering.buffering.maxResponseBodyBytes=0'
- 'traefik.http.routers.dubby.middlewares=dubby-buffering'

If you use Traefik’s file-based configuration:

http:
routers:
dubby:
rule: 'Host(`dubby.example.com`)'
entryPoints:
- websecure
service: dubby
tls:
certResolver: letsencrypt
services:
dubby:
loadBalancer:
servers:
- url: 'http://SERVER_IP_ADDRESS:3000'

After configuring your proxy, verify the headers are forwarded correctly:

  1. Open your browser’s developer tools (Network tab)
  2. Load your Dubby instance through the proxy
  3. Check the server logs — rate-limited requests should show your real IP, not the proxy’s

You can also hit the health endpoint directly:

Terminal window
curl -I https://dubby.example.com/health/

A working setup returns 200 OK with content-type: application/json.