Migrate ingress-nginx proxy-read-timeout to Envoy Gateway

Tested on RHEL 10.2 (Coughlan)
Package kubectl v1.37.0
Envoy Gateway v1.9.2
ingress-nginx controller v1.15.1
Gateway API CRDs v1.6.1 (standard channel)
Applies to Any host with kubectl configured; any Kubernetes cluster that already runs ingress-nginx and Envoy Gateway
Assumes An existing ingress-nginx Ingress and an Envoy Gateway HTTPRoute for the same application
Privilege Normal user
Scope How proxy-read-timeout behaves, why streamIdleTimeout is the closer inactivity control, and why timeouts.request is an absolute deadline. Does not cover slow uploads, idle keep-alive connections, or WebSocket stream tuning.
Related guides Fix an Envoy Gateway 504
Fix a slow Envoy Gateway upload
Migrate ingress-nginx to Envoy Gateway
Gateway API with HTTPRoute
Expose services with Ingress
Short on time?Beta Get the gist of this 6-minute read in seconds, then ask follow-up questions

nginx.ingress.kubernetes.io/proxy-read-timeout does not become timeouts.request on an HTTPRoute. One is the gap allowed between reads of the upstream response. The other is a deadline for the whole request. Copying 60 onto timeouts.request: 60s can cut a long download or a streamed response that nginx was still willing to carry, as long as the backend kept writing.

Do not translate the number until you know which behavior you need:

  • A backend that stays silent until the end makes the two look the same
  • A backend that keeps sending data does not
proxy-read-timeout is a gap between upstream reads

data ----10s---- data ----10s---- data
No 60-second silent gap, so the response can continue

HTTPRoute timeouts.request is an absolute deadline

start ------------------------------ 60s
                                     deadline

What replaces proxy-read-timeout?

ingress-nginx Envoy Gateway Same behavior?
proxy-read-timeout streamIdleTimeout, the quiet period on the stream Closest inactivity match
proxy-connect-timeout Backend TCP connect timeout Closest match
proxy-send-timeout No single annotation equivalent Depends on the request
No absolute request deadline HTTPRoute timeouts.request Different. This is a total deadline
Per-try upstream timeout timeouts.backendRequest Different scope. It cannot exceed request
client-body-timeout requestReceivedTimeout Related, and not the same. That is the slow upload case

streamIdleTimeout and timeouts.request are both still in play:

  • streamIdleTimeout is how long a stream may sit with no upstream or downstream activity. That is the same idea as a gap between reads. On Envoy Gateway v1.9 it is BackendTrafficPolicy timeout.http.streamIdleTimeout, not a field on the HTTPRoute.
  • timeouts.request is a separate deadline. Envoy Gateway's default is about 15 seconds. Raising only the idle gap does not remove it. A response that produces nothing for 16 seconds still hits the request clock. The 15 second 504 page is that failure.

How proxy-read-timeout behaves

The annotation is a whole number of seconds:

  • It does not take an s suffix
  • The controller default is 60

The Ingress below is that setting, shown at 60 seconds:

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

ingress-nginx turns that into proxy_read_timeout. Two details matter when you read a failure:

  • The timer resets when the upstream sends more of the response. Activity keeps the request alive past 60 seconds.
  • The timeout applies to each upstream read attempt. Retries can make the client wait longer than the annotation. On this controller, proxy-read-timeout: "3" with the default of 3 tries returned 504 after 9.021 seconds. Setting proxy-next-upstream-tries to 1 made the same sleep return the nginx HTML 504 in 3.025 seconds.

Do not treat the annotation as the total time the client will always wait.

A 16 second sleep, with the default 60 second read timeout and no bytes in between, still returned HTTP 200 and slept=16.0. The gap never reached 60 seconds, so nginx waited for the app.


Set the inactivity timeout on Envoy Gateway

For a stream that should survive as long as data keeps moving, set streamIdleTimeout to that gap. 60s below is the same gap as proxy-read-timeout: "60", not a new total deadline:

yaml
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: stream-idle
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: web
  timeout:
    http:
      streamIdleTimeout: 60s

Replace 60s with the quiet period you actually want. This policy does not replace timeouts.request:

  • If the route has no request timeout, Envoy Gateway v1.9.2 still stops a silent response at about 15 seconds with HTTP 504 and the body upstream request timeout
  • Set the request deadline as well when the whole call must be allowed to run longer

The other two annotations are not this gap:

  • proxy-connect-timeout is the time allowed to open the upstream connection. The default on this controller is 5 seconds. Match it with the backend connect timeout if new connections are what you were limiting.
  • proxy-send-timeout is the gap between writes of the request to the upstream. There is no one field that means that for every workload.

Troubleshooting

Symptom Likely cause What to change
A long download dies at about 15 seconds after the move timeouts.request is still the default absolute deadline Raise timeouts.request if the whole call must run that long. streamIdleTimeout alone does not remove the 15 second clock
A streamed response that used to work now dies at exactly timeouts.request The number from proxy-read-timeout was copied onto the absolute deadline Put the gap on streamIdleTimeout. Use timeouts.request only for the total call
nginx waited about three times proxy-read-timeout The read timeout was retried. Here, 3 tries turned 3 seconds into 9.021 seconds Check proxy-next-upstream-tries before you copy the client-visible wait
The failure is HTTP 408 while the body is still uploading That is the receive clock, not the upstream read gap Use the slow-upload page, not proxy-read-timeout

Summary

proxy-read-timeout is the gap between upstream reads. The closest Envoy Gateway control is streamIdleTimeout. timeouts.request is a deadline for the whole request, and the v1.9.2 default is about 15 seconds.

A 16 second silent response still succeeded through ingress-nginx at the default 60 second read timeout. The same shape of wait is what Envoy cuts with HTTP 504 when the request deadline is left at 15 seconds. Set both clocks when a long, active response must survive the move. Do not copy the annotation onto timeouts.request and call it the same setting.


References


Frequently Asked Questions

Does proxy-read-timeout 60 become timeouts.request 60s?

No. The annotation is a gap between upstream reads. timeouts.request is a deadline for the whole request. A response that keeps sending data can run longer than proxy-read-timeout. The same response stops when timeouts.request is reached.

What is the closest Envoy Gateway field?

streamIdleTimeout on a BackendTrafficPolicy. It is the time a stream may have no activity. You still need timeouts.request when the call must be allowed to run past the default request deadline of about 15 seconds.

Why did a 3 second read timeout take about 9 seconds?

The timeout is per upstream attempt. With 3 tries, the client waited 9.021 seconds. One try returned the nginx 504 in 3.025 seconds. The annotation was 3 seconds in both cases.

Why did a 16 second sleep succeed on ingress-nginx?

The default proxy-read-timeout is 60 seconds, and the app wrote nothing until the end. The gap stayed under 60 seconds, so the response was HTTP 200 and slept=16.0.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with 15+ years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)