DockLogDockLogBlog
4 min readDockLog

Putting DockLog behind nginx or Caddy

TLS, WebSocket forwarding, TRUST_PROXY, and the settings we bother with before sharing a URL.

DISABLE_AUTH=true on a home network is fine. Putting the same setup on the internet is not.

DockLog with the Docker socket mounted is effectively root on that host. With a kubeconfig, it's whatever that credential can do. Treat login like infra access, not like sharing a Grafana dashboard.

Baseline compose (auth on, loopback only)

yaml
services:
  docklog:
    image: aimldev/docklog:latest
    container_name: docklog
    ports:
      - "127.0.0.1:8888:8000"
    environment:
      - SECRET_KEY=${SECRET_KEY}
      - DB_PATH=/app/data/docklog.db
      - CLIENT_ACCESS=strict
      - ENV=production
      - TRUST_PROXY=true
      - ALLOWED_ORIGINS=https://docklog.example.com
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data:/app/data
    restart: unless-stopped

127.0.0.1:8888: only the reverse proxy on the same machine can reach it.

SECRET_KEY: openssl rand -hex 32, keep it in .env, not git.

ALLOWED_ORIGINS: must match the URL people type in the browser, including https://. Login failures behind a proxy are often just a mismatch here plus missing TRUST_PROXY.

Multiple origins (e.g. app and web) are comma-separated:

text
ALLOWED_ORIGINS=https://docklog.example.com,https://docklog.internal.example.com

Full compose walkthrough: docker-compose production setup.

nginx

Log streaming uses WebSockets. Forget the upgrade headers and tails connect then die after a few seconds.

nginx
server {
    listen 443 ssl http2;
    server_name docklog.example.com;

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

    location / {
        proxy_pass http://127.0.0.1:8888;
        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;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_read_timeout 86400;
        proxy_send_timeout 86400;
    }
}

That proxy_read_timeout matters. Default 60s kills long tail sessions.

HTTP redirect

nginx
server {
    listen 80;
    server_name docklog.example.com;
    return 301 https://$host$request_uri;
}

Optional: basic rate limit

Not a substitute for auth, but slows credential stuffing:

nginx
limit_req_zone $binary_remote_addr zone=docklog:10m rate=10r/s;

server {
    # ... ssl ...
    location / {
        limit_req zone=docklog burst=20 nodelay;
        # ... proxy_pass block ...
    }
}

Caddy

Less config, same idea:

caddy
docklog.example.com {
    reverse_proxy 127.0.0.1:8888
}

Caddy handles TLS and WebSocket upgrades automatically. Still need TRUST_PROXY=true and ALLOWED_ORIGINS on the DockLog side.

For multiple hostnames:

caddy
docklog.example.com, docklog.internal.example.com {
    reverse_proxy 127.0.0.1:8888
}

Match every hostname in ALLOWED_ORIGINS.

Kubernetes ingress

Same WebSocket mistake at ingress. Annotations vary by controller; nginx ingress example:

yaml
metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-read-timeout: "86400"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "86400"

DockLog in-cluster: K8s log tailing.

TRUST_PROXY behavior

When TRUST_PROXY=true, DockLog trusts X-Forwarded-For, X-Forwarded-Proto, and related headers from the proxy.

SettingWrong symptom
TRUST_PROXY off behind TLS proxyCookies/redirects use http://
TRUST_PROXY on with public port exposedSpoofed client IP (bind loopback only)
ALLOWED_ORIGINS missing https://Login loop after successful password

Only bind DockLog to loopback when a reverse proxy terminates TLS on the same host.

  • HTTPS only, redirect HTTP
  • Default admin password changed
  • Users created with allowed_containers patterns (RBAC post)
  • Only the ALLOW_* actions you actually want (ALLOW_SHELL is rarely worth it)
  • Hit Test on a notification channel if you set up alerts
  • Back up ./data/docklog.db somewhere off the box
  • Webhook URLs in notification channels are secrets; rotate if leaked

Optional hardening

MeasureNotes
VPN or Tailscale onlyNo public URL at all
IP allowlist at nginxallow 203.0.113.0/24; deny all;
Separate admin vs read-only usersRBAC patterns, not shared login
Audit enabledDB_PATH set, review periodically

Official checklist: security hardening guide.

When it misbehaves

SymptomFix
Login works on :8888 locally, fails on public URLALLOWED_ORIGINS or TRUST_PROXY
Logs freeze after a minuteProxy timeout, bump to 86400s
403 on API callsCLIENT_ACCESS=strict; browser client only
Everyone sees every containerUser patterns, not proxy issue
WebSocket 400/502Missing Upgrade headers (nginx) or ingress timeout
Mixed content warningsUser bookmarked http://; force HTTPS redirect

Compose walkthrough: docker-compose-production-setup. Official notes: security hardening, reverse proxy. Why socket access still matters: self-hosted log viewer.

Continue reading