Envoy Gateway
Envoy Gateway is a Kubernetes Gateway API controller that uses Envoy Proxy to handle incoming traffic. You can use it to publish HTTP, HTTPS, gRPC, TCP, and UDP services, configure routing by domain and path, manage TLS, and apply traffic policies using standard Kubernetes resources.
Gateway API is the evolution of Ingress. Instead of a single Ingress resource, it splits responsibilities across several dedicated objects:
GatewayClassdefines which controller will manage the gateways.Gatewaydescribes the entry point: ports, protocols, and TLS settings.HTTPRoutedefines HTTP traffic routing rules to backend services.GRPCRoute,TCPRoute,UDPRoute,TLSRoutehandle other traffic types.
Envoy Gateway watches these resources and uses them to create an Envoy Proxy that accepts external traffic and routes it to services inside the cluster.
Installation Copy link
To install Envoy Gateway:
- Go to the Kubernetes section and click on the cluster.
- Navigate to the Addons tab and select Envoy Gateway.
- (Optional) Enable Advanced setup to edit
values.yaml. For most use cases, the defaults are a good starting point. - Click Install.
Wait for the process to complete and verify that the Envoy Gateway pods are running:
kubectl get pods -n envoy-gateway-systemAll pods should show a Running status.
You can also confirm that the Gateway API resources are available in the cluster:
kubectl api-resources | grep gateway.networking.k8s.ioUsage Example Copy link
This example shows how to configure Envoy Gateway to accept HTTP traffic on a single external IP and route requests to two different services:
http://app.example.com/service1 routes to service1http://app.example.com/service2 routes to service2
Start by creating a dedicated namespace:
kubectl create namespace envoy-exampleCreate a ConfigMap Copy link
Create a file called configmap.yaml with HTML pages for both services:
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-pages
namespace: envoy-example
data:
service1.html: |
<html>
<head><title>Service 1</title></head>
<body><h1>Service 1</h1></body>
</html>
service2.html: |
<html>
<head><title>Service 2</title></head>
<body><h1>Service 2</h1></body>
</html>Apply the manifest:
kubectl apply -f configmap.yamlCreate Deployments and Services Copy link
Create service1.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: service1
namespace: envoy-example
spec:
replicas: 2
selector:
matchLabels:
app: service1
template:
metadata:
labels:
app: service1
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: nginx-pages
items:
- key: service1.html
path: index.html
---
apiVersion: v1
kind: Service
metadata:
name: service1
namespace: envoy-example
spec:
selector:
app: service1
ports:
- name: http
port: 80
targetPort: 80
type: ClusterIPCreate service2.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: service2
namespace: envoy-example
spec:
replicas: 2
selector:
matchLabels:
app: service2
template:
metadata:
labels:
app: service2
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: nginx-pages
items:
- key: service2.html
path: index.html
---
apiVersion: v1
kind: Service
metadata:
name: service2
namespace: envoy-example
spec:
selector:
app: service2
ports:
- name: http
port: 80
targetPort: 80
type: ClusterIPApply both manifests:
kubectl apply -f service1.yaml
kubectl apply -f service2.yamlVerify the pods are running:
kubectl get pods -n envoy-exampleCreate a GatewayClass Copy link
GatewayClass connects the Gateway API to the Envoy Gateway controller.
Create gatewayclass.yaml:
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: envoy-gateway
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controllerApply it:
kubectl apply -f gatewayclass.yamlCheck the status:
kubectl get gatewayclass envoy-gatewayThe ACCEPTED column should show True, which means Envoy Gateway has registered the GatewayClass and will manage resources associated with it.
Create a Gateway Copy link
The Gateway resource defines the external entry point for your application.
Create gateway.yaml:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: app-gateway
namespace: envoy-example
spec:
gatewayClassName: envoy-gateway
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: SameApply it:
kubectl apply -f gateway.yamlOnce the Gateway is created, Envoy Gateway will provision an Envoy Proxy and a load balancer. You can track the load balancer creation in the Hostman dashboard, or run:
kubectl get gateway app-gateway -n envoy-exampleWait until PROGRAMMED shows True and ADDRESS shows an external IP.
You can also inspect the service that was created:
kubectl get svc -n envoy-gateway-system \
-l gateway.envoyproxy.io/owning-gateway-namespace=envoy-example,gateway.envoyproxy.io/owning-gateway-name=app-gatewayCreate an HTTPRoute Copy link
HTTPRoute defines the routing rules.
Create httproute.yaml:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
namespace: envoy-example
spec:
parentRefs:
- name: app-gateway
hostnames:
- app.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /service1
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: service1
port: 80
- matches:
- path:
type: PathPrefix
value: /service2
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: service2
port: 80Apply it:
kubectl apply -f httproute.yamlConfirm the route is accepted:
kubectl get httproute app-route -n envoy-example -o yamlThe resource status should include:
status: "True" type: AcceptedThis HTTPRoute accepts requests for app.example.com and routes them to different services based on the path prefix. The URLRewrite filter strips the /service1 and /service2 prefixes, replacing them with /, so Nginx inside each service serves its main page correctly.
Verify the Setup Copy link
Get the external IP of the Gateway:
kubectl get gateway app-gateway -n envoy-exampleAdd this IP as an A record for app.example.com in your DNS settings. Once DNS propagates, test both endpoints:
curl http://app.example.com/service1
curl http://app.example.com/service2If DNS hasn't propagated yet, you can test by IP using the Host header:
curl -H "Host: app.example.com" http://<EXTERNAL_IP>/service1
curl -H "Host: app.example.com" http://<EXTERNAL_IP>/service2Both requests should return the respective Service 1 and Service 2 pages.
Clean Up Copy link
To remove all resources created in this example, run:
kubectl delete namespace envoy-example
kubectl delete gatewayclass envoy-gatewayThis does not uninstall the Envoy Gateway add-on. To remove it, go to the Addons tab in the cluster dashboard.
Migrating from Nginx Ingress to Envoy Gateway Copy link
You can run Envoy Gateway alongside Nginx Ingress and migrate routes gradually. This lets you test Gateway API resources on a separate external IP before switching DNS records or redirecting traffic to the new Gateway.
To automate the conversion of existing Ingress resources, use the ingress2gateway utility. It reads Ingress resources from your cluster and outputs equivalent Gateway API resources: Gateway, HTTPRoute, and related objects.
Prerequisites Copy link
Before migrating, make sure your cluster is ready:
- Envoy Gateway is installed.
- A
GatewayClassfor Envoy Gateway has been created. - Existing Ingress resources are working correctly through Nginx Ingress.
Check that both classes are present:
kubectl get ingressclass
kubectl get gatewayclassThe output should include an IngressClass named nginx and a GatewayClass named envoy-gateway.
Install ingress2gateway Copy link
If you have Go installed locally, run:
go install github.com/kubernetes-sigs/ingress2gateway@v1.0.0The binary will be placed in $(go env GOPATH)/bin. Make sure this directory is in your PATH.
Alternatively, install via Homebrew:
brew install ingress2gatewayOr download a prebuilt binary from the project releases page.
Source Setup Copy link
In this example, the migration targets an application already published through Nginx Ingress.
The cluster has a namespace called ingress-migration-demo containing:
- An Ingress resource named
migration-source - Services
service1andservice2 - Two routing rules for the domain
ingress-migration.example.com:/service1routes toservice1/service2routes toservice2
Verify the source Ingress:
kubectl get ingress -n ingress-migration-demoThe CLASS column should show nginx.
Convert Ingress Resources Copy link
To convert the Ingress to Gateway API resources, run:
ingress2gateway print \
--providers=ingress-nginx \
--emitter=envoy-gateway \
--ingress-nginx-ingress-class=nginx \
--namespace=ingress-migration-demo > gateway-migration.yamlThe flags in this command:
printoutputs the generated resources to stdout; the>redirect saves them togateway-migration.yaml.--providers=ingress-nginxspecifies the source Ingress controller type.--emitter=envoy-gatewaysets Envoy Gateway as the target controller.--ingress-nginx-ingress-class=nginxfilters for Ingress resources using thenginxclass.--namespace=ingress-migration-demoscopes the conversion to the source namespace.
Review and Fix the Output Copy link
Open gateway-migration.yaml and review the generated resources before applying them.
Pay attention to the gatewayClassName field in the Gateway resource. The tool may generate this value based on the source IngressClass name, resulting in something like:
spec:
gatewayClassName: nginxEnvoy Gateway will not process a Gateway with this class name unless a GatewayClass named nginx exists in the cluster. Update it to match the GatewayClass you created earlier:
spec:
gatewayClassName: envoy-gatewayApply the Gateway API Resources Copy link
Once you're satisfied with the output, apply it:
kubectl apply -f gateway-migration.yamlVerify the Gateway has been created and has received an external IP:
kubectl get gateway -ACheck that the HTTPRoute has been accepted by the controller:
kubectl get httproute -A
kubectl describe httproute <route-name> -n <namespace>The HTTPRoute status should show both Accepted=True and ResolvedRefs=True.
Test Before Switching Traffic Copy link
While your DNS records still point to the old Nginx Ingress, you can verify the new Gateway by sending requests directly to its external IP with a Host header:
curl -H "Host: app.example.com" http://<EXTERNAL_IP>/<path>Where:
app.example.comis the domain from yourHTTPRoute.<EXTERNAL_IP>is the IP address of the newGateway.<path>is the path handled by the route.
Compare the responses from the old Ingress and the new Gateway. If everything looks correct, update your DNS records to point to the new load balancer's IP.