Notice:
This is the "latest" release of Envoy Gateway, which contains the most recent commits from the main branch.
This release might not be stable.
Please refer to the /docs documentation for the most current information.

GRPC Timeouts

Unlike HTTPRoute, the Gateway API GRPCRoute resource does not (yet) expose a native timeouts field — see the upstream tracking issue Support Request Timeouts for GRPCRoute. Until that lands, Envoy Gateway lets you configure timeouts for gRPC traffic with a BackendTrafficPolicy that targets the GRPCRoute.

The default request timeout is 15 seconds in Envoy Proxy, which will terminate long-lived streaming RPCs. The relevant spec.timeout.http fields are:

  • requestTimeout: the maximum duration for the entire response to be received from the upstream. This bounds unary RPCs. Set it to "0s" to disable it for streaming RPCs, which otherwise would be cut off once the timeout elapses.
  • maxStreamDuration: the maximum duration of a stream, measured from when the request is sent until the response stream is fully consumed. It does not apply to non-streaming requests. Set it to "0s" to allow streams to run indefinitely.
  • streamIdleTimeout: the amount of time a stream may exist with no upstream or downstream activity. Use this to reclaim idle streams without capping a healthy long-lived stream’s total duration.

Prerequisites

Follow the steps below to install Envoy Gateway and the example manifest. Before proceeding, you should be able to query the example backend using HTTP.

Expand for instructions
  1. Install the Gateway API CRDs and Envoy Gateway using Helm:

    helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace
    
  2. Install the GatewayClass, Gateway, HTTPRoute and example app:

    kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/latest/quickstart.yaml -n default
    
  3. Verify Connectivity:

    Get the External IP of the Gateway:

    export GATEWAY_HOST=$(kubectl get gateway/eg -o jsonpath='{.status.addresses[0].value}')
       

    Curl the example app through Envoy proxy:

    curl --verbose --header "Host: www.example.com" http://$GATEWAY_HOST/get
       

    The above command should succeed with status code 200.

    Get the name of the Envoy service created the by the example Gateway:

    export ENVOY_SERVICE=$(kubectl get svc -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}')
       

    Get the deployment of the Envoy service created the by the example Gateway:

    export ENVOY_DEPLOYMENT=$(kubectl get deploy -n envoy-gateway-system --selector=gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=eg -o jsonpath='{.items[0].metadata.name}')
       

    Port forward to the Envoy service:

    kubectl -n envoy-gateway-system port-forward service/${ENVOY_SERVICE} 8888:80 &
       

    Curl the example app through Envoy proxy:

    curl --verbose --header "Host: www.example.com" http://localhost:8888/get
       

    The above command should succeed with status code 200.

Follow the GRPC Routing task to set up a Gateway and a GRPCRoute named yages before configuring timeouts.

Note: A GRPCRoute can have at most one BackendTrafficPolicy attached to it; a second policy targeting the same route is rejected as Conflicted. The two examples below are therefore alternatives that reuse the same policy name (grpc-timeouts) — pick the one that matches your workload. Re-applying with the same metadata.name updates the existing policy rather than creating a conflicting second one.

Unary RPCs

Set requestTimeout to bound the duration of unary RPCs. Here, unary calls that take longer than 5 seconds are terminated with a timeout.

cat <<EOF | kubectl apply -f -
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: grpc-timeouts
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: GRPCRoute
    name: yages
  timeout:
    http:
      requestTimeout: "5s"
EOF

Save and apply the following resource to your cluster:

---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: grpc-timeouts
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: GRPCRoute
    name: yages
  timeout:
    http:
      requestTimeout: "5s"

Streaming RPCs

For server-streaming, client-streaming, or bidirectional-streaming RPCs, requestTimeout would terminate the stream once it elapses. Disable it with "0s" and, if you want an upper bound, use maxStreamDuration (or streamIdleTimeout to reclaim only idle streams). The example below lets streams run indefinitely while reclaiming streams that are idle for more than 1 hour.

cat <<EOF | kubectl apply -f -
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: grpc-timeouts
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: GRPCRoute
    name: yages
  timeout:
    http:
      # Disable the per-request timeout so long-lived streams are not cut off.
      requestTimeout: "0s"
      # Allow streams to run indefinitely; set a non-zero value to cap them.
      maxStreamDuration: "0s"
      # Reclaim streams with no activity for more than 1 hour.
      streamIdleTimeout: "1h"
EOF

Save and apply the following resource to your cluster:

---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: grpc-timeouts
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: GRPCRoute
    name: yages
  timeout:
    http:
      # Disable the per-request timeout so long-lived streams are not cut off.
      requestTimeout: "0s"
      # Allow streams to run indefinitely; set a non-zero value to cap them.
      maxStreamDuration: "0s"
      # Reclaim streams with no activity for more than 1 hour.
      streamIdleTimeout: "1h"

Verification

First confirm the policy is accepted:

kubectl get backendtrafficpolicy/grpc-timeouts -o yaml

The status should reflect Accepted=True on the targeted GRPCRoute ancestor.

Then confirm the timeout is actually programmed into the Envoy route config with egctl:

egctl config envoy-proxy route \
  --labels gateway.envoyproxy.io/owning-gateway-name=eg,gateway.envoyproxy.io/owning-gateway-namespace=default \
  -o yaml | grep -A2 -E 'timeout|maxStreamDuration'

For the unary example you should see the route’s timeout set to the configured requestTimeout (e.g. timeout: 5s). For the streaming example you should see timeout: 0s (disabled) together with maxStreamDuration on the route action.

To exercise the timeout end-to-end you need a gRPC backend that can delay or stream (the sample yages echo server used in the GRPC Routing task returns immediately). Against such a backend, a unary call that exceeds requestTimeout returns gRPC status DEADLINE_EXCEEDED (HTTP 504), for example with grpcurl:

grpcurl -plaintext -authority=grpc-example.com ${GATEWAY_HOST}:80 <your.slow.Method>
# ERROR:
#   Code: DeadlineExceeded