Repository navigation
1.0-25 — Dashboard live streams behind nginx
Dashboard live streams survive a reverse proxy
The dashboard log tails went silent behind nginx and filled its error log with
upstream timed out (110: Connection timed out) while reading response header from upstream,
request: "GET /deploy/dashboard/agentlog/events"
No configuration change is needed. Upgrading the server jar is enough; nginx defaults
(proxy_read_timeout 60s, proxy_buffering on) now work as they are.
What was wrong
/deploy/dashboard/agentlog/events and /deploy/dashboard/log/events staged their response headers
and then wrote nothing until a new log line appeared. Vert.x flushes the status line and the headers
together with the first body write, so on a quiet server not a single byte left the socket. nginx
waited out its proxy_read_timeout and dropped the connection while still reading the response
header, the browser's EventSource reconnected, and the loop repeated for as long as the dashboard
stayed open. The Live screen was never affected: it pushes a full card snapshot on connect and
on every tick.
What changed
All three streams ({DASHBOARD_PATH}/events, /log/events, /agentlog/events) now:
- open with a
: connectedcomment, which flushes the status line and headers immediately; - send a
: keepalivecomment after 15 s of silence, four times inside the 60 s nginx default, so an
idle tail never trips a proxy idle timeout; - set
X-Accel-Buffering: no, so nginx streams the events through instead of buffering them.
SSE comments are ignored by EventSource, so the vendored htmx client is unchanged.
Behind a second proxy
If the deploy server sits behind an outer proxy as well, that one must also pass the stream through
unbuffered — it either honours X-Accel-Buffering or needs proxy_buffering off. Otherwise the
events still arrive, but in bursts. A minimal location:
location /deploy {
proxy_pass http://127.0.0.1:9090;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
}See Behind a reverse proxy
in the docs.
Verification
An integration test now asserts that both tail endpoints answer 200 text/event-stream with
X-Accel-Buffering: no and deliver their opening comment while the logs are still empty — the exact
case that used to hang until the proxy timed out.