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:

  • GatewayClass defines which controller will manage the gateways.
  • Gateway describes the entry point: ports, protocols, and TLS settings.
  • HTTPRoute defines HTTP traffic routing rules to backend services.
  • GRPCRoute, TCPRoute, UDPRoute, TLSRoute handle 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:

  1. Go to the Kubernetes section and click on the cluster.
  2. Navigate to the Addons tab and select Envoy Gateway.
  3. (Optional) Enable Advanced setup to edit values.yaml. For most use cases, the defaults are a good starting point.
  4. Click Install.

Wait for the process to complete and verify that the Envoy Gateway pods are running:

kubectl get pods -n envoy-gateway-system

All 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.io

Usage 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 service1
  • http://app.example.com/service2 routes to service2

Start by creating a dedicated namespace:

kubectl create namespace envoy-example

Create 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.yaml

Create 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: ClusterIP

Create 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: ClusterIP

Apply both manifests:

kubectl apply -f service1.yaml
kubectl apply -f service2.yaml

Verify the pods are running:

kubectl get pods -n envoy-example

Create 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-controller

Apply it:

kubectl apply -f gatewayclass.yaml

Check the status:

kubectl get gatewayclass envoy-gateway

The 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: Same

Apply it:

kubectl apply -f gateway.yaml

Once 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-example

Wait 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-gateway

Create 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: 80

Apply it:

kubectl apply -f httproute.yaml

Confirm the route is accepted:

kubectl get httproute app-route -n envoy-example -o yaml

The resource status should include:

status: "True" type: Accepted

This 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-example

Add 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/service2

If 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>/service2

Both 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-gateway

This 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 GatewayClass for 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 gatewayclass

The 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.0

The binary will be placed in $(go env GOPATH)/bin. Make sure this directory is in your PATH.

Alternatively, install via Homebrew:

brew install ingress2gateway

Or 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 service1 and service2
  • Two routing rules for the domain ingress-migration.example.com:
    • /service1 routes to service1
    • /service2 routes to service2

Verify the source Ingress:

kubectl get ingress -n ingress-migration-demo

The 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.yaml

The flags in this command:

  • print outputs the generated resources to stdout; the > redirect saves them to gateway-migration.yaml.
  • --providers=ingress-nginx specifies the source Ingress controller type.
  • --emitter=envoy-gateway sets Envoy Gateway as the target controller.
  • --ingress-nginx-ingress-class=nginx filters for Ingress resources using the nginx class.
  • --namespace=ingress-migration-demo scopes 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: nginx

Envoy 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-gateway

Apply the Gateway API Resources
Copy link

Once you're satisfied with the output, apply it:

kubectl apply -f gateway-migration.yaml

Verify the Gateway has been created and has received an external IP:

kubectl get gateway -A

Check 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.com is the domain from your HTTPRoute.
  • <EXTERNAL_IP> is the IP address of the new Gateway.
  • <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.