The Gateway Chronicles: Teaching Envoy to Talk to Your App
Overview
Envoy Gateway is a Kubernetes-native API gateway built on top of Envoy Proxy which has functions for network and traffic management. It’s designed to work seamlessly with the Kubernetes Gateway API and takes a batteries-included approach—offering built-in support for authentication, authorization, rate limiting, CORS handling, header manipulation, and more. These capabilities are exposed through familiar Kubernetes-style APIs, letting you fully tap into Envoy’s power without needing complex configurations. In essence, the Gateway API provides a standard interface. Envoy Gateway adds production-grade capabilities to that interface, bridging the gap between simplicity and power while keeping everything Kubernetes-native.
Correlation Gateway API with Envoy
The relationship between these custom resources and the standard resources of the Gateway API is illustrated in the diagram below:
Envoy Gateway Parts
Envoy Gateway is a system made up of two main parts:
- A
data plane, which handles the actual network traffic - A
control plane, which manages and configures the data plane
Envoy Gateway Architecture

Components
Envoy Gateway is made up of several components that communicate in-process; how this communication happens is described in the Watching Components Design.
Provider
A Provider is an infrastructure component that Envoy Gateway calls to establish its runtime configuration, resolve services, persist data, etc. As of v0.2, Kubernetes is the only implemented provider. A provider is configured at start up through Envoy Gateway’s static configuration.
Kubernetes Provider
- Uses Kubernetes-style controllers to reconcile Kubernetes resources that comprise the dynamic configuration.
- Manages the data plane through Kubernetes API CRUD operations.
- Uses Kubernetes for Service discovery.
- Uses etcd (via Kubernetes API) to persist data.
File Provider
- Uses a file watcher to watch files in a directory that define the data plane configuration.
- Manages the data plane by calling internal APIs, e.g. CreateDataPlane().
- Uses the host’s DNS for Service discovery.
- If needed, the local filesystem is used to persist data.
Resource Watcher
- The Resource Watcher watches resources used to establish and maintain Envoy Gateway’s dynamic configuration. The mechanics for watching resources is provider-specific, e.g. informers, caches, etc. are used for the Kubernetes provider. The Resource Watcher uses the configured provider for input and provides resources to the Resource Translator as output.
Resource Translator
The Resource Translator translates external resources, e.g. GatewayClass, from the Resource Watcher to the Intermediate Representation (IR). It is responsible for:
- Translating infrastructure-specific resources/fields from the Resource Watcher to the Infra IR.
- Translating proxy configuration resources/fields from the Resource Watcher to the xDS IR.
Note
The Resource Translator is implemented as the Translator API type in the gatewayapi package.
Intermediate Representation (IR)
The Intermediate Representation defines internal data models that external resources are translated into. This allows Envoy Gateway to be decoupled from the external resources used for dynamic configuration. The IR consists of an Infra IR used as input for the Infra Manager and an xDS IR used as input for the xDS Translator.
- Infra IR- Used as the internal definition of the managed data plane infrastructure.
- xDS IR- Used as the internal definition of the managed data plane xDS configuration.
xDS Translator
The xDS Translator translates the xDS IR into xDS Resources that are consumed by the xDS server.
xDS Server
The xDS Server is a xDS gRPC Server based on Go Control Plane. Go Control Plane implements the Delta xDS Server Protocol and is responsible for using xDS to configure the data plane.
Infra Manager
The Infra Manager is a provider-specific component responsible for managing the following infrastructure:
- Data Plane - Manages all the infrastructure required to run the managed Envoy proxies. For example, CRUD Deployment, Service, etc. resources to run Envoy in a Kubernetes cluster.
- Auxiliary Control Planes - Optional infrastructure needed to implement application Gateway features that require external integrations with the managed Envoy proxies. For example, Global Rate Limiting requires provisioning and configuring the Envoy Rate Limit Service and the Rate Limit filter. Such features are exposed to users through the Custom Route Filters extension. The Infra Manager consumes the Infra IR as input to manage the data plane infrastructure.
Use Cases
Use The Gateway API to:
- Define how external traffic enters and is routed within your cluster
- Configure HTTP(S), TLS, and TCP traffic routing in a standardized, Kubernetes-native way
- Apply host-based, path-based, and header-based routing rules using HTTPRoute
- Terminate TLS at the edge using Gateway TLS configuration
- Separate responsibilities between infrastructure and application teams through role-oriented resource design
- Improve portability and consistency across different gateway implementations
Objectives
In this article, we’ll walk through the step-by-step installation of the Envoy Gateway on a local Kubernetes cluster and create a sample mechanism to route the traffic to the sample app.
Prerequisites
- A Kubernetes cluster
- Kubectl
- Helm
Installation
Install the Gateway API CRDs and Envoy Gateway:
helm install eg oci://docker.io/envoyproxy/gateway-helm --version v1.6.0 -n envoy-gateway-system --create-namespaceWait for Envoy Gateway to become available:
kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=AvailableApply the GatewayClass, Gateway, HTTPRoute and example app:
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: eg
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: eg
spec:
gatewayClassName: eg
listeners:
- name: http
protocol: HTTP
port: 80
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: backend
---
apiVersion: v1
kind: Service
metadata:
name: backend
labels:
app: backend
service: backend
spec:
ports:
- name: http
port: 3000
targetPort: 3000
selector:
app: backend
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
replicas: 1
selector:
matchLabels:
app: backend
version: v1
template:
metadata:
labels:
app: backend
version: v1
spec:
serviceAccountName: backend
containers:
- image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e
imagePullPolicy: IfNotPresent
name: backend
ports:
- containerPort: 3000
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: backend
spec:
parentRefs:
- name: eg
hostnames:
- "www.corenux-test.com"
rules:
- backendRefs:
- group: ""
kind: Service
name: backend
port: 3000
weight: 1
matches:
- path:
type: PathPrefix
value: /Testing the Configuration
Without LoadBalancer Support
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}')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.corenux-test.com" http://localhost:8888/getOutput
* Host localhost:8888 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:8888...
* Connected to localhost (::1) port 8888
> GET /get HTTP/1.1
> Host: www.corenux-test.com
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
Handling connection for 8888
< HTTP/1.1 200 OK
< content-type: application/json
< x-content-type-options: nosniff
< date: Mon, 17 Nov 2025 11:21:26 GMT
< content-length: 471
<
{
"path": "/get",
"host": "www.corenux-test.com",
"method": "GET",
"proto": "HTTP/1.1",
"headers": {
"Accept": [
"*/*"
],
"User-Agent": [
"curl/8.7.1"
],
"X-Envoy-External-Address": [
"127.0.0.1"
],
"X-Forwarded-For": [
"10.10.10.2"
],
"X-Forwarded-Proto": [
"http"
],
"X-Request-Id": [
"0aa2f6d7-f94b-4ab3-8429-aec5beec0cda"
]
},
"namespace": "default",
"ingress": "",
"service": "",
"pod": "backend-77d4d5968-r4cxc"
* Connection #0 to host localhost left intact
}Check Envoy Default Gateway Logs
{
":authority":"www.corenux-test.com",
"bytes_received":0,
"bytes_sent":471,
"connection_termination_details":null,
"downstream_local_address":"127.0.0.1:10080",
"downstream_remote_address":"127.0.0.1:33138",
"duration":2,
"method":"GET",
"protocol":"HTTP/1.1",
"requested_server_name":null,
"response_code":200,
"response_code_details":"via_upstream",
"response_flags":"-",
"route_name":"httproute/default/backend/rule/0/match/0/www_corenux-test_com",
"start_time":"2025-11-17T11:21:26.178Z",
"upstream_cluster":"httproute/default/backend/rule/0",
"upstream_host":"10.10.10.32:3000",
"upstream_local_address":"10.10.10.2:36304",
"upstream_transport_failure_reason":null,
"user-agent":"curl/8.7.1",
"x-envoy-origin-path":"/get",
"x-envoy-upstream-service-time":null,
"x-forwarded-for":"10.10.10.2",
"x-request-id":"0aa2f6d7-f94b-4ab3-8429-aec5beec0cda"
}Results
In this quickstart, you have:
- Installed Envoy Gateway
- Deployed a backend service, and a gateway
- Configured the gateway using Kubernetes Gateway API resources Gateway and HTTPRoute to direct incoming requests over HTTP to the backend service.