- AI Gateway
- next
- Resiliency
Configuring Timeouts¶
This guide explains how to configure timeouts for the API Platform Gateway so that requests to slow or unreachable backends, and slow downstream clients, fail within a predictable time instead of hanging indefinitely.
Timeouts are configured at two levels:
| Level | Where it is configured | Scope | Typical use |
|---|---|---|---|
| Gateway level | Inside the gateway config.toml |
Global defaults applied to all traffic on the gateway | Operator-wide baselines (connect timeout, default route timeout, downstream/HCM timeouts) |
| API level | The API definition — the resilience block and upstreamDefinitions[].timeout.connect |
A single API, its routes, and its upstream clusters | Per-API/per-route overrides for backends that are faster or slower than the global default |
The gateway level establishes the defaults. The API level overrides those defaults for a specific API. When an API does not specify a value, the gateway-level default applies.
Gateway-level timeouts (config.toml)¶
These timeouts are global defaults that apply to every request handled by the gateway, regardless of which API serves it. You can use gateway/configs/config-template.toml as a reference when creating your own config.toml.
There are two groups:
- Upstream timeouts (
[router.upstream.timeouts]) — govern the connection to, and the response from, the backend. - HTTP Connection Manager (HCM) timeouts (
[router.http_listener.timeouts]) — govern the downstream connection between the client and the gateway.
Upstream timeouts¶
[router.upstream.timeouts]
route_timeout_ms = 60000 # Default per-route request→response timeout
route_idle_timeout_ms = 300000 # Default per-route stream idle timeout
connect_timeout_ms = 5000 # TCP connection establishment timeout
| Setting | Default | Maps to (Envoy) | Description |
|---|---|---|---|
connect_timeout_ms |
5000 |
Cluster connect_timeout |
How long the gateway waits to establish a TCP connection to an upstream endpoint before failing the request. |
route_timeout_ms |
60000 |
RouteAction.timeout |
Default maximum time for the entire request→upstream-response on a route. This is the default that an API's resilience.timeout overrides. |
route_idle_timeout_ms |
300000 |
RouteAction.idle_timeout |
Default per-route stream idle timeout. This is the default that an API's resilience.idleTimeout overrides. |
Note
All three values must be positive (greater than 0). To disable a route timeout for a specific API, set its resilience value to 0s (see API-level timeouts).
HTTP Connection Manager (downstream) timeouts¶
These govern the connection between the client and the gateway. A value of "0s" disables the corresponding timeout.
# HTTP Connection Manager (downstream) timeouts
[router.http_listener.timeouts]
request_timeout = "0s" # Max duration for the entire downstream request
request_headers_timeout = "0s" # Max duration to receive the complete request headers
stream_idle_timeout = "5m" # Idle timeout for a single HTTP stream/request
idle_timeout = "1h" # Idle timeout for the downstream connection
| Setting | Default | Maps to (Envoy) | Description |
|---|---|---|---|
request_timeout |
0s (disabled) |
HCM request_timeout |
Max time for the gateway to receive the entire downstream request. |
request_headers_timeout |
0s (disabled) |
HCM request_headers_timeout |
Max time to receive the complete request headers. |
stream_idle_timeout |
5m |
HCM stream_idle_timeout |
Idle timeout for a single HTTP stream/request. |
idle_timeout |
1h |
HCM common_http_protocol_options.idle_timeout |
Idle timeout for the downstream connection. |
Durations use Go duration syntax with a single unit (for example "30s", "500ms", "5m", "1h"). If a value is left unset, the Envoy default for that field applies.
Warning
idle_timeout can also be disabled by setting it to zero explicitly. Disabling it has a high likelihood of yielding connection leaks (for example, due to lost TCP FIN packets).
Setting gateway-level timeouts in different deployments¶
Standalone / local
Configure the timeouts directly in gateway/configs/config.toml using the structure shown above.
Kubernetes with Helm
When deploying the gateway via the Helm chart, the same settings are controlled through Helm values under gateway.config.router.*. Example values.yaml snippet:
gateway:
config:
router:
upstream:
timeouts:
connect_timeout_ms: 5000
route_timeout_ms: 60000
route_idle_timeout_ms: 300000
http_listener:
timeouts:
request_timeout: "0s"
request_headers_timeout: "0s"
stream_idle_timeout: "5m"
idle_timeout: "1h"
The chart renders these values into the generated config.toml used by the gateway controller.
API-level timeouts¶
Two of the timeout layers are naturally per-API and can be tuned directly in the API definition, each through its own field:
| Surface | Field(s) | Maps to (Envoy) | Overrides gateway default |
|---|---|---|---|
Connect timeout per upstream (upstreamDefinitions[].timeout.connect) |
connect |
Cluster connect_timeout |
connect_timeout_ms |
resilience block |
timeout, idleTimeout |
RouteAction.timeout / idle_timeout |
route_timeout_ms / route_idle_timeout_ms |
The HCM (downstream) timeouts have no per-API equivalent and remain gateway-level only.
Connect timeout per upstream (upstreamDefinitions[].timeout.connect)¶
When an API routes through a named upstream definition, that definition can set its own connect timeout, which overrides the gateway-level connect_timeout_ms for the cluster built from it. Because the setting lives on the upstream definition, different upstreams used by the same API can have different connect timeouts.
apiVersion: gateway.api-platform.wso2.com/v1
kind: LlmProvider
metadata:
name: openai-provider
spec:
displayName: OpenAI Provider
version: v1.0
template: openai
context: /openai
upstreamDefinitions:
- name: openai-backend
timeout:
connect: 6000ms # overrides connect_timeout_ms for this cluster
upstreams:
- url: https://api.openai.com/v1
upstream:
ref: openai-backend # this provider routes through the openai-backend cluster
accessControl:
mode: deny_all
exceptions:
- path: /chat/completions
methods: [POST]
The same shape applies to the Mcp kind — declare upstreamDefinitions at the spec level and point upstream.ref at the definition by name.
connectis a duration string (for example6000ms,5s).- It governs only TCP connection establishment (plus the TLS handshake for HTTPS upstreams) — not the response wait, which is the route timeout below.
- An API that uses a direct
upstream.url(instead of anupstreamDefinitionsref) uses the gateway-levelconnect_timeout_ms.
Note
upstreamDefinitions — and therefore the per-upstream connect timeout — are supported only for the LlmProvider and Mcp kinds. LlmProxy does not connect to a backend directly (it loops back to its backing provider), so it has no upstream definition of its own. To bound connection establishment for a proxied call, configure the connect timeout on the backing LlmProvider to match your requirements.
Route timeouts (via resilience block)¶
The resilience block lets an individual API override the gateway-level route timeouts for its own traffic. It maps to Envoy's RouteAction timeouts.
Note
For all three kinds — LlmProvider, LlmProxy, and Mcp — the resilience block is supported at the API level only. There is no operation-level override; a single API-level block applies to every route generated for the API.
It supports two fields:
| Field | Maps to (Envoy) | Overrides gateway default |
|---|---|---|
timeout |
RouteAction.timeout |
route_timeout_ms |
idleTimeout |
RouteAction.idle_timeout |
route_idle_timeout_ms |
Duration format
timeout and idleTimeout take single-unit duration strings (for example 30s, 500ms, 1.5m). Use 0s to disable a timeout, or omit the field to fall back to the gateway-level default. Compound (1h30m), negative (-30s), and unitless (0, 30) values are rejected.
Sample LLM Provider, LLM Proxy, and MCP:¶
For LlmProvider and LlmProxy, the resilience block is supported at the API level only. LLM routes are generated by the gateway from the access-control configuration and policy attachments (rather than authored as explicit operations), so there is no operation-level override. A single API-level block applies to all routes generated for the LLM API.
apiVersion: gateway.api-platform.wso2.com/v1
kind: LlmProvider
metadata:
name: openai-provider
spec:
displayName: OpenAI Provider
version: v1.0
template: openai
context: /openai
upstream:
url: https://api.openai.com/v1
accessControl:
mode: deny_all
exceptions:
- path: /chat/completions
methods: [POST]
# Applies to every route generated for this provider.
resilience:
timeout: 60s
idleTimeout: 30s
apiVersion: gateway.api-platform.wso2.com/v1
kind: LlmProxy
metadata:
name: openai-proxy
spec:
displayName: OpenAI Proxy
version: v1.0
context: /openai-proxy
provider:
id: openai-provider
resilience:
timeout: 75s
LLM Proxy double hop
A request to a proxy traverses two routes — client → proxy route → (loopback) → provider route → backend. The proxy's resilience.timeout bounds the whole proxied call, while the backing provider's resilience.timeout bounds the provider→backend call. For a meaningful end-to-end budget, keep the proxy timeout greater than or equal to the provider timeout; the shorter of the two effectively wins.
For Mcp, the resilience block applies to the traffic-forwarding routes generated for the proxy. Because MCP transports are long-lived streams, MCP treats the route timeout differently from the other kinds (see the note below).
apiVersion: gateway.api-platform.wso2.com/v1
kind: Mcp
metadata:
name: everything-mcp
spec:
displayName: Everything MCP
version: v1.0
context: /everything
upstream:
url: https://mcp.example.com
# Applies to the traffic-forwarding routes generated for this MCP proxy.
resilience:
idleTimeout: 30m
MCP route timeout defaults to disabled
MCP transports (SSE / streamable HTTP) are long-lived streams, so a finite route timeout would sever an otherwise healthy stream. For Mcp, the route timeout therefore defaults to disabled (0s) when the resilience block does not set it — unlike the other kinds, which fall back to the gateway-level route_timeout_ms. Liveness on a stalled stream is instead bounded by idleTimeout. You can still set timeout explicitly if you need a hard cap, but be aware it will cut long-lived streams. The per-upstream connect timeout still applies to MCP as usual.
Practical guidance¶
Connect timeout (connect_timeout_ms)¶
- Decrease when backends are highly available and you want to fail fast on unhealthy or misconfigured targets, freeing resources quickly.
- Increase when backends sit behind slower networks/load balancers, or may experience cold starts or scaling events that briefly delay connection establishment.
- Avoid setting it too low — it may cause false-positive timeouts during short periods of backend slowness or network jitter.
- Set the gateway default for the common case, and override per upstream via
upstreamDefinitions[].timeout.connect(see Connect timeout per upstream) when a specific backend needs a different connection budget.
Route timeout (route_timeout_ms / resilience.timeout)¶
- Set the gateway default to a sane upper bound for typical backends, and use API level
resilience.timeoutfor endpoints that are known to be faster (tighter budget) or legitimately slower (e.g. report generation, LLM completions). - For streaming/long-lived responses (e.g. SSE from LLM backends), be mindful of
idleTimeout: a long totaltimeoutwith a shortidleTimeoutcan still cut a slow token stream. LeaveidleTimeoutgenerous (or unset) for streaming providers.
HCM/downstream timeouts¶
request_headers_timeoutis a useful protection against slow-header (Slowloris-style) clients; enable it with a small value (for example"5s") if your clients are expected to send headers promptly.request_timeoutis disabled by default; enable it only if you want a hard cap on how long the gateway will spend receiving an entire downstream request.
Example scenarios¶
A slow-to-connect backend¶
A backend may take a few seconds to accept new connections during peak load. Give connections more time than the default while still failing in a bounded time when the backend is unreachable:
The gateway gives each upstream connection attempt up to 6 seconds; if the backend is down, requests fail after ~6 seconds (503) instead of hanging.
Disabling the route timeout for a long-running API¶
Disabling the route timeout removes only the per-route response deadline — it does not make the request truly unbounded. The request is still subject to every other applicable timeout: the upstream connect_timeout_ms, the route idleTimeout, and the downstream HCM stream_idle_timeout / idle_timeout, any of which can still terminate a stalled stream or connection. In other words, behaviour is bounded by all applicable gateway- and API-level timeouts. Use with care.