Timeouts in ClickHouse are not a single knob. There are at least eight settings that influence whether a client stays connected long enough to receive its result, and they cover different layers: TCP keep-alive, application-level send/receive, HTTP framing, and query execution. Most "my query disconnects after N seconds" reports trace back to a mismatch between one of these limits and the actual time the query needs. This article maps the settings to the failure modes they cause.
The settings that matter
ClickHouse exposes timeouts at three layers. Knowing which layer fires first saves a lot of guessing.
| Setting | Layer | What it controls |
|---|---|---|
receive_timeout |
Server, per-connection | Maximum idle time waiting to read from a client. Slow or stalled clients get disconnected. |
send_timeout |
Server, per-connection | Maximum idle time waiting to write to a client. Triggers if the client stops reading. |
tcp_keep_alive_timeout |
TCP | Application-level keep-alive interval. Stops middleboxes from dropping idle TCP sessions. |
http_receive_timeout |
HTTP | HTTP equivalent of receive_timeout. |
http_send_timeout |
HTTP | HTTP equivalent of send_timeout. |
keep_alive_timeout |
HTTP 1.1 | How long the server holds a keep-alive socket open between requests. Defaults to 10s in 23.12+, was 3s before. |
http_connection_timeout |
HTTP outbound | Timeout for ClickHouse connecting to other servers. Does not affect inbound clients. |
http_headers_progress_interval_ms |
HTTP | How often the server emits progress headers during long queries. |
http_wait_end_of_query |
HTTP | If set, the server waits for the query to finish before closing the HTTP response. |
max_execution_time |
Query | Maximum wall time for query execution. Different from connection timeouts. |
sync_request_timeout |
Server | Timeout for synchronous internal calls like Ping or TableStatus. Default 5 seconds. |
The key distinction: max_execution_time controls how long a query may run on the server. The receive/send/keep-alive family controls whether the client connection survives that long. A query can finish inside max_execution_time and still fail if the HTTP keep-alive closed the socket first.
Failure mode: HTTP idle disconnect during long queries
Symptom: a query that takes 30 seconds returns successfully via clickhouse-client, but the same query over HTTP through a load balancer or JDBC driver dies after a few seconds with a broken pipe or read error.
Cause: between the time the server starts executing and the time it produces the first byte of output, the HTTP socket is idle. If keep_alive_timeout or an intermediate proxy idle limit is shorter than the query's pre-output phase, the socket gets torn down.
Fix: have the server send progress updates so the connection is never truly idle.
SET send_progress_in_http_headers = 1;
SET http_headers_progress_interval_ms = 10000;
SET http_wait_end_of_query = 1;
For JDBC, pass these through the URL:
jdbc:clickhouse://host:8123/db?custom_settings=send_progress_in_http_headers=1,http_headers_progress_interval_ms=10000
http_wait_end_of_query makes the server hold the response open until the query completes, which prevents premature closure when results are buffered or empty.
Failure mode: receive_timeout closes the connection
Symptom: a client streaming an INSERT or running a long query that produces no output for a while sees Read timeout or Connection reset by peer.
Cause: receive_timeout (default 300s) closes connections where the server is waiting on the client. If you are inserting a large batch with slow producer code, or the client process is paused, the server gives up.
Fix: increase the relevant timeouts. These must be set in the default user profile because they take effect at connection establishment, not mid-session.
In users.d/long_running.xml:
<clickhouse>
<profiles>
<default>
<receive_timeout>3600</receive_timeout>
<send_timeout>3600</send_timeout>
<tcp_keep_alive_timeout>3600</tcp_keep_alive_timeout>
<http_headers_progress_interval_ms>10000</http_headers_progress_interval_ms>
<http_wait_end_of_query>1</http_wait_end_of_query>
</default>
</profiles>
</clickhouse>
Setting these per-query with SET is too late for the values that govern session establishment.
Failure mode: NAT or load balancer kills idle TCP
Symptom: connections that sit idle for several minutes between queries silently die, and the next query fails. This is common on AWS NLBs (350s idle timeout) and corporate firewalls.
Cause: the network device drops the TCP flow because it sees no traffic. The application-level keep_alive_timeout does not help here because keep-alive is between requests on the same socket, not while one is in flight.
Fix: enable TCP keep-alive so packets flow even during idle periods.
<tcp_keep_alive_timeout>60</tcp_keep_alive_timeout>
This makes ClickHouse emit TCP keep-alive probes at the specified interval. Set the value lower than the NAT or LB idle timeout.
Inspecting current values
To see what is actually in effect for the current session:
SELECT name, value, changed, description
FROM system.settings
WHERE name ILIKE '%timeout%' OR name ILIKE '%keep_alive%'
ORDER BY name;
changed = 1 means the value differs from the compiled default and was set via config or SET.
Common Pitfalls
- Setting
receive_timeoutwithSETafter the connection is open. The session already has its values; raise them in the default profile or pass via session parameters at connect time. - Tuning only
max_execution_timeto fix a disconnect. Execution time is unrelated to whether the HTTP socket stays alive. - Forgetting that HTTP clients sit behind load balancers with their own idle timeouts. Configure both ends.
- Leaving
http_wait_end_of_queryoff when query results are produced only at the end. The socket can close while the client is still waiting. - Confusing
http_connection_timeout(outbound, used by ClickHouse calling other servers) with the HTTP receive/send timeouts that apply to inbound traffic.
Frequently Asked Questions
Q: What is the difference between receive_timeout and keep_alive_timeout?
A: receive_timeout applies during an active request when the server is waiting for the client to send more data. keep_alive_timeout applies between HTTP requests on a persistent connection. They cover different phases of the connection lifecycle.
Q: Should I just set every timeout to a huge value? A: No. Timeouts protect the server from leaked sockets and runaway sessions. Raise only the ones that actually fire for your workload. The defaults are reasonable for most cases.
Q: How do I pass these settings from a JDBC or HTTP client?
A: Use the custom_settings query parameter or the HTTP header X-ClickHouse-Settings. Example: ?custom_settings=send_progress_in_http_headers=1,http_headers_progress_interval_ms=10000. Driver-specific properties also exist for connect, socket, and read timeouts on the client side; those need to be aligned with server values.
Q: Does max_execution_time cancel the query or just terminate the connection?
A: It cancels the query. The server stops processing and returns an error to the client. The TCP connection itself is not torn down by max_execution_time.
Q: Why does my JDBC connection time out before my server-side receive_timeout?
A: The JDBC driver has its own socket and read timeouts, often defaulted to 30 seconds. The driver enforces the lower of the two values. Configure socket_timeout (and similar) on the driver to match.