Setting Up Envoy Gateway in Kubernetes
Setting Up Envoy Gateway in Kubernetes
Envoy Gateway is a Kubernetes Gateway API implementation built around the Envoy proxy. It provides a modern way to expose Kubernetes applications through HTTP/HTTPS routing, while keeping application Services internal to the cluster.
This guide walks through a complete setup:
Client → Envoy Gateway → Gateway → HTTPRoute → Kubernetes Service → Application Pod
The examples are intentionally simple so that you can use them as a starting point for a development or test Kubernetes cluster.
1. What is Envoy Gateway?
Traditionally, Kubernetes applications are exposed using resources such as Ingress:
Internet
|
v
Ingress Controller
|
v
Kubernetes Service
|
v
Application PodEnvoy Gateway uses the Kubernetes Gateway API, which separates infrastructure configuration from application routing.
The architecture becomes:
Kubernetes Cluster
┌─────────────────────────────────────────────────────┐
│ │
│ Client │
│ │ │
│ │ HTTP/HTTPS │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ Envoy Gateway │ │
│ │ / Envoy Proxy │ │
│ └───────────┬───────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Gateway │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ HTTPRoute │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Kubernetes │ │
│ │ Service │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Application │ │
│ │ Pod │ │
│ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────┘The Gateway API defines resources such as GatewayClass, Gateway, and HTTPRoute. HTTPRoute is a GA resource in the Gateway API and is used to define HTTP routing from a Gateway to Kubernetes Services. (Gateway API)
2. Main Envoy Gateway Components
There are several important Kubernetes resources involved.
GatewayClass
Defines which controller manages the Gateway.
kind: GatewayClassFor Envoy Gateway, this normally points to the Envoy Gateway controller.
Gateway
Defines the network entry point.
For example:
kind: GatewayA Gateway can define:
- HTTP listener
- HTTPS listener
- TLS configuration
- allowed namespaces
- ports
- hostnames
HTTPRoute
Defines how HTTP traffic should be routed.
For example:
example.com/api
|
v
HTTPRoute
|
v
backend-service:8080An HTTPRoute references a Gateway using parentRefs and specifies backend Services using backendRefs. (Gateway API)
3. Prerequisites
You need:
- A running Kubernetes cluster
kubectl- Helm
- Network access to install Envoy Gateway
- A
LoadBalancerimplementation if you want an externally accessible Gateway
For a cloud Kubernetes cluster, such as EKS, GKE or AKS, a LoadBalancer implementation is normally available.
For bare-metal or local Kubernetes, you may need something such as MetalLB. Envoy’s documentation specifically recommends having a LoadBalancer implementation when the cluster does not already provide one. (Envoy Gateway)
Check your cluster:
kubectl get nodesExample:
NAME STATUS ROLES AGE
master-01 Ready control-plane 20d
worker-01 Ready <none> 20d
worker-02 Ready <none> 20dCheck Helm:
helm versionCheck Kubernetes:
kubectl version4. Install Envoy Gateway
The Envoy Gateway documentation provides Helm-based installation. The current quickstart documentation uses the Envoy Gateway Helm OCI chart. (Envoy Gateway)
Create the namespace and install Envoy Gateway:
helm install eg \
oci://docker.io/envoyproxy/gateway-helm \
--version v1.9.1 \
-n envoy-gateway-system \
--create-namespaceFor production environments, pin a tested Envoy Gateway version rather than blindly using
latest.
Check the installation:
kubectl get pods -n envoy-gateway-systemYou should see something similar to:
NAME READY STATUS RESTARTS AGE
envoy-gateway-xxxxxxxxxx-xxxxx 1/1 Running 0 1mWait until Envoy Gateway is ready:
kubectl wait --timeout=5m -n envoy-gateway-system \
deployment/envoy-gateway --for=condition=AvailableThis is also the readiness check recommended by the Envoy Gateway quickstart. (Envoy Gateway)
5. Verify Gateway API Resources
Check the Gateway API CRDs:
kubectl get crd | grep gatewayYou should see resources such as:
gatewayclasses.gateway.networking.k8s.io
gateways.gateway.networking.k8s.io
httproutes.gateway.networking.k8s.io
referencegrants.gateway.networking.k8s.ioCheck GatewayClasses:
kubectl get gatewayclassExpected:
NAME CONTROLLER
eg gateway.envoyproxy.io/gatewayclass-controllerThe exact controller output can vary with the Envoy Gateway version.
6. Create a Sample Application
Now let’s create a very simple web application.
We’ll use the ealen/echo-server image because it makes it easy to see HTTP request information.
Create:
mkdir envoy-demo
cd envoy-demoCreate application.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: envoy-demo---
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-app
namespace: envoy-demo
spec:
replicas: 2
selector:
matchLabels:
app: echo-app
template:
metadata:
labels:
app: echo-app
spec:
containers:
- name: echo-app
image: ealen/echo-server:latest
ports:
- containerPort: 80---
apiVersion: v1
kind: Service
metadata:
name: echo-service
namespace: envoy-demo
spec:
selector:
app: echo-app
ports:
- name: http
port: 80
targetPort: 80
Apply it:
kubectl apply -f application.yamlCheck the pods:
kubectl get pods -n envoy-demoExpected:
NAME READY STATUS RESTARTS AGE
echo-app-xxxxxxxxxx-xxxxx 1/1 Running 0 30s
echo-app-xxxxxxxxxx-yyyyy 1/1 Running 0 30sCheck the Service:
kubectl get svc -n envoy-demoExpected:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
echo-service ClusterIP 10.96.100.100 <none> 80/TCPNotice that the application Service is only ClusterIP.
We don’t need to expose the application itself as a NodePort or LoadBalancer.
Envoy Gateway will provide the external access.
7. Create the Gateway
Now create the actual Gateway.
Create gateway.yaml:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: envoy-gateway
namespace: envoy-demo
spec:
gatewayClassName: eg listeners:
- name: http
protocol: HTTP
port: 80Apply:
kubectl apply -f gateway.yamlCheck:
kubectl get gateway -n envoy-demoExample:
NAME CLASS ADDRESS PROGRAMMED AGE
envoy-gateway eg 192.168.1.100 True 30sThe ADDRESS is particularly important.
It represents the address through which clients can reach the Gateway.
8. Create an HTTPRoute
Now we need to tell Envoy where incoming requests should go.
Create httproute.yaml:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: echo-route
namespace: envoy-demo
spec:
parentRefs:
- name: envoy-gatewayrules:
- matches:
- path:
type: PathPrefix
value: / backendRefs:
- name: echo-service
port: 80
Apply:
kubectl apply -f httproute.yamlCheck:
kubectl get httproute -n envoy-demoExpected:
NAME HOSTNAMES AGE
echo-route 30sDescribe it:
kubectl describe httproute echo-route -n envoy-demoLook for:
Accepted: Trueand:
ResolvedRefs: TrueThese conditions are useful when troubleshooting route configuration.
9. Understanding the Complete Configuration
At this point we have:
HTTP request
|
v
┌───────────────┐
│ Envoy Gateway │
│ :80 │
└───────┬───────┘
|
v
┌───────────────┐
│ HTTPRoute │
│ / │
└───────┬───────┘
|
v
┌───────────────┐
│ echo-service │
│ :80 │
└───────┬───────┘
|
┌──────┴──────┐
│ │
v v
echo-pod-1 echo-pod-2The important relationship is:
Gateway
|
+---- HTTPRoute
|
+---- Service
|
+---- PodsThis separation is one of the major advantages of Gateway API.
10. Check the Envoy Service
Envoy Gateway creates the required Envoy proxy infrastructure when the Gateway is programmed.
Check Services:
kubectl get svc -ALook for something similar to:
envoy-gateway-system envoy-envoy-gateway-xxxxxYou can also search:
kubectl get svc -A | grep envoyFor a LoadBalancer-based setup, you may see:
NAME TYPE CLUSTER-IP EXTERNAL-IP
envoy-xxxxx LoadBalancer 10.96.10.20 192.168.1.100On a cloud provider it could instead be:
EXTERNAL-IP
a123456789.elb.amazonaws.com11. Test the Application
Get the Gateway address:
kubectl get gateway envoy-gateway -n envoy-demoFor example:
NAME CLASS ADDRESS PROGRAMMED
envoy-gateway eg 192.168.1.100 TrueTest:
curl http://192.168.1.100/The request should reach:
Envoy
↓
HTTPRoute
↓
echo-service
↓
echo-appBecause we’re using an echo server, the response should contain information about the HTTP request.
12. Host-Based Routing
One of the useful features of Gateway API is hostname-based routing.
Get Damindu Sandaruwan Bandara’s stories in your inbox
Join Medium for free to get updates from this writer.
For example:
api.example.com
|
v
api-serviceweb.example.com
|
v
web-serviceModify the Gateway:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: envoy-gateway
namespace: envoy-demo
spec:
gatewayClassName: eg listeners:
- name: http
protocol: HTTP
port: 80
hostname: "*.example.com"Then create:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: echo-route
namespace: envoy-demo
spec:
parentRefs:
- name: envoy-gatewayhostnames:
- "echo.example.com" rules:
- matches:
- path:
type: PathPrefix
value: / backendRefs:
- name: echo-service
port: 80
Now the route is associated with:
echo.example.comTest using:
curl \
--header "Host: echo.example.com" \
http://192.168.1.100/The Gateway API supports hostname matching before evaluating the HTTPRoute rules. (Envoy Gateway)
13. Path-Based Routing
You can also route different paths to different applications.
For example:
example.com/api
|
v
api-serviceexample.com/web
|
v
web-serviceExample:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: application-route
namespace: envoy-demo
spec:
parentRefs:
- name: envoy-gatewayrules: - matches:
- path:
type: PathPrefix
value: /api backendRefs:
- name: api-service
port: 8080 - matches:
- path:
type: PathPrefix
value: /web backendRefs:
- name: web-service
port: 8080
This allows one Gateway to expose multiple applications.
The Gateway API HTTP routing guide uses the same basic concept: HTTPRoute rules can match request paths and forward traffic to different Kubernetes Services. (Gateway API)
14. Traffic Splitting
Gateway API can also distribute traffic between backend versions.
For example:
HTTP
|
v
Envoy Gateway
|
HTTPRoute
|
┌───────┴────────┐
| |
v v
v1 Service v2 Service
90% 10%Example:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: echo-route
namespace: envoy-demo
spec:
parentRefs:
- name: envoy-gatewayrules:
- backendRefs:
- name: echo-v1
port: 80
weight: 90 - name: echo-v2
port: 80
weight: 10
This is useful for:
- Canary deployments
- Blue/green deployments
- Application migrations
- A/B testing
Gateway API supports weighted backend references for this type of traffic splitting. (Envoy Gateway)
15. HTTPS / TLS
For production applications, HTTP should normally be replaced or supplemented with HTTPS.
The architecture becomes:
Client
|
| HTTPS :443
|
v
Envoy Gateway
|
| HTTP
|
v
Application ServiceA Gateway can have an HTTPS listener:
listeners:
- name: https
protocol: HTTPS
port: 443tls:
mode: Terminate certificateRefs:
- name: example-tls
The TLS Secret might look like:
kubectl create secret tls example-tls \
--cert=tls.crt \
--key=tls.key \
-n envoy-demoThen the Gateway terminates TLS and forwards normal HTTP traffic to the backend.
For production, certificates can be managed automatically using tools such as cert-manager.
16. Cross-Namespace Routing
One important Gateway API feature is the separation between infrastructure and applications.
For example:
envoy-gateway-system
|
|
Envoy Gatewayplatform namespace
|
Gatewayapplication namespace
|
HTTPRoute
|
Service
An HTTPRoute doesn’t automatically get permission to reference resources across namespaces. Gateway API provides mechanisms such as allowedRoutes and ReferenceGrant to control these relationships.
For example:
listeners:
- name: http
protocol: HTTP
port: 80 allowedRoutes:
namespaces:
from: AllThis means HTTPRoutes from other namespaces can attach to the listener.
A more restrictive configuration could be:
allowedRoutes:
namespaces:
from: SameThis means only Routes from the same namespace can attach.
This is particularly important in multi-team Kubernetes clusters.
17. Useful Troubleshooting Commands
If the Gateway isn’t working, start with:
kubectl get gatewayclassThen:
kubectl get gateway -AThen:
kubectl get httproute -ACheck Gateway details:
kubectl describe gateway envoy-gateway -n envoy-demoCheck the HTTPRoute:
kubectl describe httproute echo-route -n envoy-demoCheck Envoy Gateway:
kubectl get pods -n envoy-gateway-systemCheck logs:
kubectl logs \
-n envoy-gateway-system \
deployment/envoy-gatewayFind Envoy Services:
kubectl get svc -A | grep envoyCheck endpoints:
kubectl get endpoints -n envoy-demoOr:
kubectl get endpointslices -n envoy-demo18. Common Problems
Gateway has no ADDRESS
Check:
kubectl get gateway -n envoy-demoIf you see:
ADDRESS <none>check the generated Envoy Service:
kubectl get svc -A | grep envoyIf the Service is:
ClusterIPor has no external address, your Kubernetes cluster may not have a LoadBalancer implementation.
On bare-metal clusters, installing MetalLB is one possible solution, as recommended in the Envoy Gateway documentation. (Envoy Gateway)
HTTPRoute shows Accepted: False
Run:
kubectl describe httproute echo-route -n envoy-demoLook at:
Status:
Parents:Typical causes include:
- Wrong Gateway name
- Wrong namespace
- Gateway doesn’t allow the Route namespace
- Invalid backend Service
- Invalid backend port
- Gateway listener mismatch
404 from Envoy
A 404 often means the request reached Envoy but did not match the configured route.
Check:
kubectl describe httproute echo-route -n envoy-demoIf using a hostname:
hostnames:
- echo.example.comtest with:
curl \
-H "Host: echo.example.com" \
http://<GATEWAY-IP>/Without the correct Host header, the HTTPRoute may not match.
503 from Envoy
A 503 generally indicates that Envoy cannot successfully reach the backend.
Check:
kubectl get svc -n envoy-demoThen:
kubectl get endpoints -n envoy-demoYou should see pod IPs associated with:
echo-serviceAlso verify:
kubectl get pods -n envoy-demoand:
kubectl describe pod <pod-name> -n envoy-demo19. Complete Example
For a simple development environment, you can keep the configuration in three files.
application.yaml
apiVersion: v1
kind: Namespace
metadata:
name: envoy-demo---
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-app
namespace: envoy-demo
spec:
replicas: 2
selector:
matchLabels:
app: echo-app
template:
metadata:
labels:
app: echo-app
spec:
containers:
- name: echo-app
image: ealen/echo-server:latest
ports:
- containerPort: 80---
apiVersion: v1
kind: Service
metadata:
name: echo-service
namespace: envoy-demo
spec:
selector:
app: echo-app
ports:
- port: 80
targetPort: 80
gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: envoy-gateway
namespace: envoy-demo
spec:
gatewayClassName: eg listeners:
- name: http
protocol: HTTP
port: 80httproute.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: echo-route
namespace: envoy-demo
spec:
parentRefs:
- name: envoy-gatewayrules:
- matches:
- path:
type: PathPrefix
value: / backendRefs:
- name: echo-service
port: 80
Deploy everything:
kubectl apply -f application.yaml
kubectl apply -f gateway.yaml
kubectl apply -f httproute.yamlThen:
kubectl get gateway -n envoy-demoGet the Gateway address and test:
curl http://<GATEWAY-ADDRESS>/20. Final Architecture
After completing the setup, your Kubernetes environment looks like this:
Internet
|
| HTTP / HTTPS
v
┌──────────────────────┐
│ LoadBalancer │
└──────────┬───────────┘
|
v
┌──────────────────────┐
│ Envoy Proxy │
│ │
│ Managed by Envoy │
│ Gateway │
└──────────┬───────────┘
|
v
┌──────────────────────┐
│ Gateway │
│ │
│ HTTP :80 │
│ HTTPS :443 │
└──────────┬───────────┘
|
v
┌──────────────────────┐
│ HTTPRoute │
│ │
│ / │
│ /api │
│ /web │
└──────────┬───────────┘
|
┌──────────────┼──────────────┐
| | |
v v v
api-service web-service other-service
| | |
v v v
Pods Pods PodsConclusion
Envoy Gateway provides a clean separation between platform networking and application routing. Instead of exposing every application with its own LoadBalancer or relying on a large collection of Ingress rules, a platform team can provide a shared Gateway while application teams define their own HTTPRoute resources.
The basic workflow is:
1. Install Envoy Gateway
↓
2. GatewayClass
↓
3. Create Gateway
↓
4. Deploy application
↓
5. Create Service
↓
6. Create HTTPRoute
↓
7. Test through GatewayFor production, the next logical steps are HTTPS/TLS, cross-namespace routing, authentication, rate limiting, observability, retries/timeouts, and canary traffic splitting. Envoy Gateway’s documentation also provides dedicated tasks for secure gateways, traffic management, rate limiting, and gRPC routing. (Envoy Gateway)
Official references: Envoy Gateway Quickstart · Gateway API HTTPRoute documentation · Gateway API HTTP routing guide









