> ## Content Index
> Fetch the complete content index at: https://devopscube.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Setup Envoy Gateway API Controller On Kubernetes (Beginners Guide)
- URL: https://devopscube.com/setup-envoy-gateway-api/
- Published: 2026-01-20T13:35:48.000Z
- Updated: 2026-02-03T05:14:07.000Z
- Author: Bibin Wilson
- Tags: Kubernetes, #arunlal, #syntax-highlight, #blog

In this detailed guide, you will learn how to set up and configure the **Envoy Gateway API Controller** for Gateway API implementation on **Kubernetes**.

By the end of this blog, you will learn how to:

1. Install the Envoy Gateway
2. Configure EnvoyProxy, GatewayClass, Gateway, and HTTPRoute (CRDs of the Gateway API)
3. Test the traffic routing using a demo application with the Envoy Gateway.

Before we move on to the installation, lets understand what is Envoy Gateway.

## What is Envoy Gateway?

[Envoy Gateway](https://gateway.envoyproxy.io/?ref=devopscube.com) is an open-source project that is designed to simplify the use of Envoy Proxy as an API Gateway. It is a CNCF (Community-led) project.

It acts as the controller that makes Gateway API resources work using Envoy Proxy. 

Here is an interesting thing about Envoy. Compared to other Gateway API controllers, **Envoy Gateway** stands out because it uses **Envoy Proxy**, which is built specifically for cloud-native environments.

Envoy uses a fully dynamic control plane called **xDS (Discovery Service)**. This means configuration changes, such as adding or updating a route, are applied instantly in memory. There is no need to restart or reload the proxy process.

In simple terms, traffic rules can change in real time without causing downtime. This makes Envoy **Gateway** a strong fit for modern Kubernetes and microservices setups where changes happen often and need to be safe and fast.

💡

Envoy's [xDS protocol ](https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds%5Fprotocol?ref=devopscube.com)has become the ****de facto standard** for [service mesh](https://devopscube.com/service-mesh-tools/) like [Istio](https://devopscube.com/setup-istio-ambient-mode/) and cloud-native proxy configuration.

Also, Envoy Gateway is mainly used for **North–South traffic** and can be seen as a modern replacement for Ingress. 

From a Gateway API workflow perspective, Envoy Gateway is similar to the NGINX Gateway Fabric controller that we explained in the [Gateway API tutorial.](https://devopscube.com/kubernetes-gateway-api/)

## Envoy Gateway Workflow

Envoy Gateway has the following two main components.

1. **Gateway Controller (Control Plane)** : Primary controller that watches the Gateway API Custom Resources and publishes the routing rules and configs to the Proxy controller.
2. **Proxy Controller (Data Plane):** This is the actual traffic routing proxy controller created by the Gateway controller to route the traffic to the intended services.

Now lets understand the workflow of the Envoy Gateway.

The following diagram illustrates the workflow of the Envoy Gateway API on Kubernetes.

![Envoy Gateway Controller Workflow With Gateway API](https://storage.ghost.io/c/5f/2f/5f2f4d20-2abf-4534-8d40-7aa233aedd43/content/images/2026/01/image-45.png)

Here is how it works.

1. The cluster admin creates a Gateway (and related resources like `HTTPRoute`) using the Kubernetes Gateway API CRDs.
2. The Envoy Gateway controller watches these resources and configures the Envoy Proxy data plane. In most setups, it also creates a Kubernetes Service of type LoadBalancer, which triggers your cloud provider to provision an external load balancer.
3. Based on your `HTTPRoute` rules, Envoy is configured with the right routing logic to reach your backend workloads.
4. When a user sends a request, it first hits the external load balancer, then gets forwarded to the Envoy Proxy pods.
5. Envoy then routes the traffic to the correct backend Service, and from there Kubernetes forwards it to the right application pods.

So the flow looks like the following.

Client – >External Load Balancer – >Envoy Proxy (data plane) – >Kubernetes Service – >Application Pods

Now that you understand how Envoy Gateway works, let’s get started with the setup.

## Prerequisites 

The following are the requirements to install the Envoy Gateway on the Kubernetes cluster.

1. A [Kubernetes Cluster](https://devopscube.com/setup-kubernetes-cluster-kubeadm/)
2. [Kubectl](https://devopscube.com/kubectl-set-context/) (Local Workstation)
3. [Helm](https://devopscube.com/create-helm-chart/) (Local Workstation)

I assume you have a Kubernetes cluster to start the Envoy installation. If not, refer to this --> [**Create AWS EKS Cluster Using eksctl**](https://devopscube.com/create-aws-eks-cluster-eksctl/)

## Install Envoy Gateway using Helm

Follow the steps given below to install the Envoy Gateway using the Helm chart.

### Step 1: Install the Envoy Gateway & Gateway API CRD's

We can directly install the Gateway API CRDs and Envoy Gateway if you don't want to make any customization.

Execute Helm command for the installation.

```bash
helm install eg oci://docker.io/envoyproxy/gateway-helm --version v1.6.2 -n envoy-gateway-system --create-namespace

```

The installation will take a few minutes to complete. During that time, we can check the Gateway controller deployment status.

For reference, below is the structure of the Envoy Gateway [Helm chart](https://devopscube.com/setup-prometheus-helm-chart/) used for the installation.

```bash
envoy-gateway-helm
├── Chart.yaml
├── README.md
├── crds
│   ├── gatewayapi-crds.yaml
│   └── generated
│       ├── gateway.envoyproxy.io_backends.yaml
│       ├── gateway.envoyproxy.io_backendtrafficpolicies.yaml
│       ├── gateway.envoyproxy.io_clienttrafficpolicies.yaml
│       ├── gateway.envoyproxy.io_envoyextensionpolicies.yaml
│       ├── gateway.envoyproxy.io_envoypatchpolicies.yaml
│       ├── gateway.envoyproxy.io_envoyproxies.yaml
│       ├── gateway.envoyproxy.io_httproutefilters.yaml
│       └── gateway.envoyproxy.io_securitypolicies.yaml
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── _rbac.tpl
│   ├── certgen-rbac.yaml
│   ├── certgen.yaml
│   ├── envoy-gateway-config.yaml
│   ├── envoy-gateway-deployment.yaml
│   ├── envoy-gateway-hpa.yaml
│   ├── envoy-gateway-poddisruptionbudget.yaml
│   ├── envoy-gateway-rbac.yaml
│   ├── envoy-gateway-service.yaml
│   ├── envoy-gateway-serviceaccount.yaml
│   ├── envoy-proxy-topology-injector-webhook.yaml
│   ├── infra-manager-rbac.yaml
│   ├── leader-election-rbac.yaml
│   ├── namespace.yaml
│   └── namespaced-infra-manager-rbac.yaml
└── values.yaml
```

The container images used on this Helm chart are.

1. `docker.io/envoyproxy/gateway:v1.6.2` \- Control Plane Controller
2. `docker.io/envoyproxy/ratelimit:99d85510` \- This will limit the request to the Envoy proxy based on the given rules.
3. `docker.io/envoyproxy/gateway-dev:latest` \- The Data Plane

⚠️

If you are setting up this in an enterprise environment, you may have to upload the images to your private container registry and then modify the helm chart to use those images for deployment. 

### Step 2: Validating Installation

We can check the deployment status of the Envoy Gateway to ensure the Control Plane controller is deployed.

```bash
$ kubectl -n envoy-gateway-system get all

NAME                                READY   STATUS    RESTARTS   AGE
pod/envoy-gateway-6dd8f9b8f-r5w47   1/1     Running   0          2m18s

NAME                    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                                            AGE
service/envoy-gateway   ClusterIP   10.110.16.99   <none>        18000/TCP,18001/TCP,18002/TCP,19001/TCP,9443/TCP   2m18s

NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/envoy-gateway   1/1     1            1           2m18s

NAME                                      DESIRED   CURRENT   READY   AGE
replicaset.apps/envoy-gateway-6dd8f9b8f   1         1         1       2m18s
```

The status (`Running`) ensures that the Control Plane Controller is ready to watch the [Gateway API](https://devopscube.com/migrate-kubernetes-ingress-to-gateway-api/) and Envoy Custom Resources and deploy Data Planes.

Before we create any Custom Resource, lets list the Custom Resource Definitions of the Gateway API as well as the Envoy Gateway.

```bash
$ kubectl get crds | grep -iE "gateway"

backends.gateway.envoyproxy.io
backendtrafficpolicies.gateway.envoyproxy.io
clienttrafficpolicies.gateway.envoyproxy.io
envoyextensionpolicies.gateway.envoyproxy.io
envoypatchpolicies.gateway.envoyproxy.io
envoyproxies.gateway.envoyproxy.io
httproutefilters.gateway.envoyproxy.io
securitypolicies.gateway.envoyproxy.io

backendtlspolicies.gateway.networking.k8s.io
gatewayclasses.gateway.networking.k8s.io
gateways.gateway.networking.k8s.io
grpcroutes.gateway.networking.k8s.io
httproutes.gateway.networking.k8s.io
referencegrants.gateway.networking.k8s.io
tcproutes.gateway.networking.k8s.io
tlsroutes.gateway.networking.k8s.io
udproutes.gateway.networking.k8s.io
xbackendtrafficpolicies.gateway.networking.x-k8s.io
xlistenersets.gateway.networking.x-k8s.io

```

Next, we will test the controller implementation using a sample application.

## Deploying a Sample Application

To test the traffic routing using Envoy Gateway, we create an [Nginx](https://devopscube.com/service-discovery-example/) deployment on the cluster.

```bash
kubectl create deployment web-deploy --image nginx --port 80 --replicas 2
```

Create a service for this deployment 

```bash
kubectl expose deployment web-deploy --name web-svc --port 80 --target-port 80 --type ClusterIP
```

Once both deployment and service creation are completed, we can check them using the following command.

```bash
$ kubectl get deploy,svc

NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/web-deploy   2/2     2            2           2m10s

NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
service/web-svc      ClusterIP   10.100.235.119   <none>        80/TCP    54s
```

Now that our demo application is ready, our next task is to setup external traffic to this application using the envoy gateway.

Let's start with creating the Envoy Proxy Custom Resource.

## Create EnvoyProxy Resource

💡

By default, ****Envoy Data Planes** use a **`LoadBalancer`** service type for Data Planes. This means that if you are running in a cloud cluster, such as [EKS](https://devopscube.com/create-aws-eks-cluster-eksctl/) or AKS, the Load Balancer will be automatically created when you deploy the `gateway` CRD.  
  
Since this is a test setup, we do not want a LoadBalancer. So we can change the Data Plane configuration to use `NodePort` using the `EnvoyProxy` CRD.   
  
This changes the default behavior of the Control Plane so that the Data Planes created by the Controller will not use the Load Balancer service type.

> **Note:** Skip the following EnvoyProxy modification if you can can use a Load Balancer to test the external traffic.

Create the `EnvoyProxy` manifest with the parameter of `type: NodePort`. This will overwrite the default configuration available on the Control Plane.

```bash
cat <<EOF> envoyproxy.yaml
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: envoy-proxy
  namespace: default
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyService:
        type: NodePort
EOF
```

Apply the Envoy Proxy on the Kubernetes cluster.

```bash
kubectl apply -f envoyproxy.yaml
```

List the EnvoyProxy resource once the installation is completed.

```bash
$ kubectl get envoyproxy

NAME          AGE
envoy-proxy   22m
```

The first step is successfully completed, so we can now create the `GatewayClass` resource and attach this Envoy Proxy.

## Create GatewayClass Resource 

A cluster can have multiple gateway API controllers. The **`GatewayClass`** Custom Resource is used to identify which gateway API controller to be used. 

So in the GatewayClass manifest, we need to mention the controller ID on the `spec.controllerName` parameter as well as the created EnvoyProxy under the `spec.parametersRef` section.

Execute the following to create `gatewayclass.yaml`

```bash
cat <<EOF> gatewayclass.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: envoy-gateway-class
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
  parametersRef:
    group: gateway.envoyproxy.io
    kind: EnvoyProxy
    name: envoy-proxy
    namespace: default
EOF
```

The important config to note here is, `gateway.envoyproxy.io/gatewayclass-controller` is the default name given to the controller. This can be modified in the Helm chart when you use multiple controllers on a same cluster.

> **Note**: Skip the `parametersRef` section, if you don't want to use the NodePort service for the Data Planes.

Apply the Gateway Class configuration.

```bash
kubectl apply -f gatewayclass.yaml
```

To check the resource creation.

```bash
$ kubectl get gatewayclass

NAME                  CONTROLLER                                      
envoy-gateway-class   gateway.envoyproxy.io/gatewayclass-controller 
```

The Gateway Class resource creation is now completed. We can now create the `Gateway` Custom Resource.

## Create Gateway Resource 

Gateway Custom Resource is like a router. When the traffic enters the cluster, it initially hits the Gateway.

When you create a Gateway resource, the Control Plane controller essentially **deploys an Envoy Proxy pod** (Data Planes). So all the routing logic will be part of this proxy.

Lets create a `Gateway` manifest for the HTTP protocol, with Port 80 traffic to enter.

```bash
cat <<EOF> gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: web-gateway
  namespace: default
spec:
  gatewayClassName: envoy-gateway-class
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: All
EOF
```

As you can see in the manifest, **Gateways are listener-specific**. This means you must clearly define which port and protocol the incoming traffic should use.

Now, apply the Gateway to the cluster.

```bash
kubectl apply -f gateway.yaml
```

To check the Gateway status, use the following command.

```bash
$ kubectl -n default get gateway

NAME          CLASS                 ADDRESS      PROGRAMMED   AGE
web-gateway   envoy-gateway-class   172.30.1.2   True         2m10s
```

Due to the modified Envoy proxy configs, the Data Plane will be deployed with the type `NodePort` service type. We can check it using the following command.

```bash
kubectl -n envoy-gateway-system get deploy,svc

```

![The output of the deployed envoy gateway pod with NodePort ](https://storage.ghost.io/c/5f/2f/5f2f4d20-2abf-4534-8d40-7aa233aedd43/content/images/2025/07/image-110.png)

**Note down your NodePort number** (`31299`) to test the application access in the upcoming section.

Now the Gateway is ready, but we need to create one more resource called an `HTTPRoute`.

## Create a HTTPRoute Resource

The `HTTPRoute` Custom Resource will help the traffic from the Gateway to the appropriate service of the application.

We need to create a HTTPRoute manifest with the following contents.

```bash
cat <<EOF> httproute.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web-httproute
spec:
  parentRefs:
    - name: web-gateway
  rules:
    - backendRefs:
        - group: ""
          kind: Service
          name: web-svc
          port: 80
          weight: 1
      matches:
        - path:
            type: PathPrefix
            value: /
EOF
```

Apply the HTTPRoute manifest.

```bash
kubectl apply -f httproute.yaml
```

Once it is deployed, we can list the available HTTPRoute resources.

```bash
$ kubectl -n default get httproute

NAME            HOSTNAMES   AGE
web-httproute               7m23s
```

We have configured everything needed to route external traffic to our application. Next is testing the traffic.

## Test the Application Traffic

We can now try to access our application using the `curl` command from the local machine. For that, we need the `NodePort` of Gateway proxy and the [IP](https://devopscube.com/ip-address-tutorial/) of one of the worker nodes.

Use the following command to see the output.

```bash
curl [NODE_IP]:[NODE_PORT_NUMBER]
```

The output will show the Nginx web page.

![the output of the demo application](https://storage.ghost.io/c/5f/2f/5f2f4d20-2abf-4534-8d40-7aa233aedd43/content/images/2025/07/image-111.png)

This ensures that the traffic is properly routed to the correct backend service through the envoy proxy.

💡

For production implementation, map the LoadBalancer DNS to a DNS service like Route53 so that you can easily access the application through a domain name.

## Advantages of Envoy Gateway

We already have many [Gateway API controllers](https://gateway-api.sigs.k8s.io/implementations/?ref=devopscube.com), so why use Envoy Gateway?

The following are some of the advantages.

1. This controller watches the Gateway API CRDs and automatically updates the Envoy proxy settings using the xDS API. The xDS API can make changes to running processes, so without downtime, we can make config changes.
2. Envoy proxy uses a **filter chain** to handle HTTP processing, rate limiting, and authentication. For this, it uses **WebAssembly (WASM),** so we can also create custom filters.
3. Popular services mesh like [**Istio**](https://devopscube.com/istio-opensource-platform-microservices-management/) use Envoy proxies, so using Envoy Gateway would be easier for the integration between them.

## Conclusion

We saw how Envoy Gateway routes external traffic to backend services using the Gateway API.

But Envoy Gateway can do much more using its custom resources. 

You can configure domain names and TLS secrets to encrypt traffic. You can also set up weighted routing for canary deployments, and method-based routing is supported as well.

Over to you.

Are you planning to implement Envoy gateway for your projects?

Are you using any other controller now?

Either way, let us know your thoughts in the comments.