{"id":357,"date":"2026-01-20T13:35:48","date_gmt":"2026-01-20T13:35:48","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=357"},"modified":"2026-01-20T13:35:48","modified_gmt":"2026-01-20T13:35:48","slug":"setup-envoy-gateway-api","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=357","title":{"rendered":"Setup Envoy Gateway API Controller On Kubernetes (Guide)"},"content":{"rendered":"<p>In this detailed guide, you will learn how to set up and configure the <strong>Envoy Gateway API Controller<\/strong> for Gateway API implementation on <strong>Kubernetes<\/strong>.<\/p>\n<p>By the end of this blog, you will learn how to:<\/p>\n<ol>\n<li>Install the Envoy Gateway<\/li>\n<li>Configure EnvoyProxy, GatewayClass, Gateway, and HTTPRoute (CRDs of the Gateway API)<\/li>\n<li>Test the traffic routing using a demo application with the Envoy Gateway.<\/li>\n<\/ol>\n<p>Before we move on to the installation, lets understand what is Envoy Gateway.<\/p>\n<p><!--kg-card-begin: html--><br \/>\n<iframe src=\"https:\/\/subscribe-forms.beehiiv.com\/e217f508-d15d-4f5b-bda2-3c7a3d529463\" class=\"beehiiv-embed\" data-test-id=\"beehiiv-embed\" frameborder=\"0\" scrolling=\"no\" style=\"width: 560px; height: 339px; margin: 0; border-radius: 0px 0px 0px 0px !important; background-color: transparent; box-shadow: 0 0 #0000; max-width: 100%;\"><\/iframe><br \/>\n<!--kg-card-end: html--><\/p>\n<h2 id=\"what-is-envoy-gateway\">What is Envoy Gateway?<\/h2>\n<p><a href=\"https:\/\/gateway.envoyproxy.io\/?ref=devopscube.com\" rel=\"noreferrer\">Envoy Gateway<\/a> 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.<\/p>\n<p>It acts as the controller that makes Gateway API resources work using Envoy Proxy. <\/p>\n<p>Here is an interesting thing about Envoy. Compared to other Gateway API controllers, <strong>Envoy Gateway<\/strong> stands out because it uses <strong>Envoy Proxy<\/strong>, which is built specifically for cloud-native environments.<\/p>\n<p>Envoy uses a fully dynamic control plane called <strong>xDS (Discovery Service)<\/strong>. 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.<\/p>\n<p>In simple terms, traffic rules can change in real time without causing downtime. This makes Envoy <strong>Gateway<\/strong> a strong fit for modern Kubernetes and microservices setups where changes happen often and need to be safe and fast.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">Envoy&#8217;s <a href=\"https:\/\/www.envoyproxy.io\/docs\/envoy\/latest\/api-docs\/xds_protocol?ref=devopscube.com\" rel=\"noreferrer\">xDS protocol <\/a>has become the <b><strong style=\"white-space: pre-wrap;\">de facto standard<\/strong><\/b> for <a href=\"https:\/\/devopscube.com\/service-mesh-tools\/\" rel=\"noreferrer\">service mesh<\/a> like <a href=\"https:\/\/devopscube.com\/setup-istio-ambient-mode\/\" rel=\"noreferrer\">Istio<\/a> and cloud-native proxy configuration.<\/div>\n<\/div>\n<p>Also, Envoy Gateway is mainly used for <strong>North\u2013South traffic<\/strong> and can be seen as a modern replacement for Ingress. <\/p>\n<p>From a Gateway API workflow perspective, Envoy Gateway is similar to the NGINX Gateway Fabric controller that we explained in the <a href=\"https:\/\/devopscube.com\/kubernetes-gateway-api\/\" rel=\"noreferrer\">Gateway API tutorial.<\/a><\/p>\n<h2 id=\"envoy-gateway-workflow\">Envoy Gateway Workflow<\/h2>\n<p>Envoy Gateway has the following two main components.<\/p>\n<ol>\n<li><strong>Gateway Controller (Control Plane)<\/strong> : Primary controller that watches the Gateway API Custom Resources and publishes the routing rules and configs to the Proxy controller.<\/li>\n<li><strong>Proxy Controller (Data Plane):<\/strong> This is the actual traffic routing proxy controller created by the Gateway controller to route the traffic to the intended services.<\/li>\n<\/ol>\n<p>Now lets understand the workflow of the Envoy Gateway.<\/p>\n<p>The following diagram illustrates the workflow of the Envoy Gateway API on Kubernetes.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2026\/01\/image-45.png\" class=\"kg-image\" alt=\"Envoy Gateway Controller Workflow With Gateway API\" loading=\"lazy\" width=\"2000\" height=\"1512\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2026\/01\/image-45.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2026\/01\/image-45.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2026\/01\/image-45.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2026\/01\/image-45.png 2374w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Here is how it works.<\/p>\n<ol>\n<li>The cluster admin creates a Gateway (and related resources like <code>HTTPRoute<\/code>) using the Kubernetes Gateway API CRDs.<\/li>\n<li>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.<\/li>\n<li>Based on your <code>HTTPRoute<\/code> rules, Envoy is configured with the right routing logic to reach your backend workloads.<\/li>\n<li>When a user sends a request, it first hits the external load balancer, then gets forwarded to the Envoy Proxy pods.<\/li>\n<li>Envoy then routes the traffic to the correct backend Service, and from there Kubernetes forwards it to the right application pods.<\/li>\n<\/ol>\n<p>So the flow looks like the following.<\/p>\n<p>Client \u2013 &gt;External Load Balancer \u2013 &gt;Envoy Proxy (data plane) \u2013 &gt;Kubernetes Service \u2013 &gt;Application Pods<\/p>\n<p>Now that you understand how Envoy Gateway works, let\u2019s get started with the setup.<\/p>\n<h2 id=\"prerequisites\">Prerequisites <\/h2>\n<p>The following are the requirements to install the Envoy Gateway on the Kubernetes cluster.<\/p>\n<ol>\n<li>A <a href=\"https:\/\/devopscube.com\/setup-kubernetes-cluster-kubeadm\/\" rel=\"noreferrer\">Kubernetes Cluster<\/a><\/li>\n<li><a href=\"https:\/\/devopscube.com\/kubectl-set-context\/\" rel=\"noreferrer\">Kubectl<\/a> (Local Workstation)<\/li>\n<li><a href=\"https:\/\/devopscube.com\/create-helm-chart\/\" rel=\"noreferrer\">Helm<\/a> (Local Workstation)<\/li>\n<\/ol>\n<p>I assume you have a Kubernetes cluster to start the Envoy installation. If not, refer to this &#8211;&gt; <a href=\"https:\/\/devopscube.com\/create-aws-eks-cluster-eksctl\/\" rel=\"noreferrer\"><strong>Create AWS EKS Cluster Using eksctl<\/strong><\/a><\/p>\n<h2 id=\"install-envoy-gateway-using-helm\">Install Envoy Gateway using Helm<\/h2>\n<p>Follow the steps given below to install the Envoy Gateway using the Helm chart.<\/p>\n<h3 id=\"step-1-install-the-envoy-gateway-gateway-api-crds\">Step 1: Install the Envoy Gateway &amp; Gateway API CRD&#8217;s<\/h3>\n<p>We can directly install the Gateway API CRDs and Envoy Gateway if you don&#8217;t want to make any customization.<\/p>\n<p>Execute Helm command for the installation.<\/p>\n<pre><code class=\"language-bash\">helm install eg oci:\/\/docker.io\/envoyproxy\/gateway-helm --version v1.6.2 -n envoy-gateway-system --create-namespace\n<\/code><\/pre>\n<p>The installation will take a few minutes to complete. During that time, we can check the Gateway controller deployment status.<\/p>\n<p>For reference, below is the structure of the Envoy Gateway <a href=\"https:\/\/devopscube.com\/setup-prometheus-helm-chart\/\" rel=\"noreferrer\">Helm chart<\/a> used for the installation.<\/p>\n<pre><code class=\"language-bash\">envoy-gateway-helm\n\u251c\u2500\u2500 Chart.yaml\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 crds\n\u2502   \u251c\u2500\u2500 gatewayapi-crds.yaml\n\u2502   \u2514\u2500\u2500 generated\n\u2502       \u251c\u2500\u2500 gateway.envoyproxy.io_backends.yaml\n\u2502       \u251c\u2500\u2500 gateway.envoyproxy.io_backendtrafficpolicies.yaml\n\u2502       \u251c\u2500\u2500 gateway.envoyproxy.io_clienttrafficpolicies.yaml\n\u2502       \u251c\u2500\u2500 gateway.envoyproxy.io_envoyextensionpolicies.yaml\n\u2502       \u251c\u2500\u2500 gateway.envoyproxy.io_envoypatchpolicies.yaml\n\u2502       \u251c\u2500\u2500 gateway.envoyproxy.io_envoyproxies.yaml\n\u2502       \u251c\u2500\u2500 gateway.envoyproxy.io_httproutefilters.yaml\n\u2502       \u2514\u2500\u2500 gateway.envoyproxy.io_securitypolicies.yaml\n\u251c\u2500\u2500 templates\n\u2502   \u251c\u2500\u2500 NOTES.txt\n\u2502   \u251c\u2500\u2500 _helpers.tpl\n\u2502   \u251c\u2500\u2500 _rbac.tpl\n\u2502   \u251c\u2500\u2500 certgen-rbac.yaml\n\u2502   \u251c\u2500\u2500 certgen.yaml\n\u2502   \u251c\u2500\u2500 envoy-gateway-config.yaml\n\u2502   \u251c\u2500\u2500 envoy-gateway-deployment.yaml\n\u2502   \u251c\u2500\u2500 envoy-gateway-hpa.yaml\n\u2502   \u251c\u2500\u2500 envoy-gateway-poddisruptionbudget.yaml\n\u2502   \u251c\u2500\u2500 envoy-gateway-rbac.yaml\n\u2502   \u251c\u2500\u2500 envoy-gateway-service.yaml\n\u2502   \u251c\u2500\u2500 envoy-gateway-serviceaccount.yaml\n\u2502   \u251c\u2500\u2500 envoy-proxy-topology-injector-webhook.yaml\n\u2502   \u251c\u2500\u2500 infra-manager-rbac.yaml\n\u2502   \u251c\u2500\u2500 leader-election-rbac.yaml\n\u2502   \u251c\u2500\u2500 namespace.yaml\n\u2502   \u2514\u2500\u2500 namespaced-infra-manager-rbac.yaml\n\u2514\u2500\u2500 values.yaml<\/code><\/pre>\n<p>The container images used on this Helm chart are.<\/p>\n<ol>\n<li><code>docker.io\/envoyproxy\/gateway:v1.6.2<\/code> &#8211; Control Plane Controller<\/li>\n<li><code>docker.io\/envoyproxy\/ratelimit:99d85510<\/code> &#8211; This will limit the request to the Envoy proxy based on the given rules.<\/li>\n<li><code>docker.io\/envoyproxy\/gateway-dev:latest<\/code> &#8211; The Data Plane<\/li>\n<\/ol>\n<div class=\"kg-card kg-callout-card kg-callout-card-yellow\">\n<div class=\"kg-callout-emoji\">\u26a0\ufe0f<\/div>\n<div class=\"kg-callout-text\">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. <\/div>\n<\/div>\n<h3 id=\"step-2-validating-installation\">Step 2: Validating Installation<\/h3>\n<p>We can check the deployment status of the Envoy Gateway to ensure the Control Plane controller is deployed.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n envoy-gateway-system get all\n\nNAME                                READY   STATUS    RESTARTS   AGE\npod\/envoy-gateway-6dd8f9b8f-r5w47   1\/1     Running   0          2m18s\n\nNAME                    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                                            AGE\nservice\/envoy-gateway   ClusterIP   10.110.16.99   &lt;none&gt;        18000\/TCP,18001\/TCP,18002\/TCP,19001\/TCP,9443\/TCP   2m18s\n\nNAME                            READY   UP-TO-DATE   AVAILABLE   AGE\ndeployment.apps\/envoy-gateway   1\/1     1            1           2m18s\n\nNAME                                      DESIRED   CURRENT   READY   AGE\nreplicaset.apps\/envoy-gateway-6dd8f9b8f   1         1         1       2m18s<\/code><\/pre>\n<p>The status (<code>Running<\/code>) ensures that the Control Plane Controller is ready to watch the <a href=\"https:\/\/devopscube.com\/migrate-kubernetes-ingress-to-gateway-api\/\" rel=\"noreferrer\">Gateway API<\/a> and Envoy Custom Resources and deploy Data Planes.<\/p>\n<p>Before we create any Custom Resource, lets list the Custom Resource Definitions of the Gateway API as well as the Envoy Gateway.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get crds | grep -iE \"gateway\"\n\nbackends.gateway.envoyproxy.io\nbackendtrafficpolicies.gateway.envoyproxy.io\nclienttrafficpolicies.gateway.envoyproxy.io\nenvoyextensionpolicies.gateway.envoyproxy.io\nenvoypatchpolicies.gateway.envoyproxy.io\nenvoyproxies.gateway.envoyproxy.io\nhttproutefilters.gateway.envoyproxy.io\nsecuritypolicies.gateway.envoyproxy.io\n\nbackendtlspolicies.gateway.networking.k8s.io\ngatewayclasses.gateway.networking.k8s.io\ngateways.gateway.networking.k8s.io\ngrpcroutes.gateway.networking.k8s.io\nhttproutes.gateway.networking.k8s.io\nreferencegrants.gateway.networking.k8s.io\ntcproutes.gateway.networking.k8s.io\ntlsroutes.gateway.networking.k8s.io\nudproutes.gateway.networking.k8s.io\nxbackendtrafficpolicies.gateway.networking.x-k8s.io\nxlistenersets.gateway.networking.x-k8s.io\n<\/code><\/pre>\n<p>Next, we will test the controller implementation using a sample application.<\/p>\n<h2 id=\"deploying-a-sample-application\">Deploying a Sample Application<\/h2>\n<p>To test the traffic routing using Envoy Gateway, we create an <a href=\"https:\/\/devopscube.com\/service-discovery-example\/\" rel=\"noreferrer\">Nginx<\/a> deployment on the cluster.<\/p>\n<pre><code class=\"language-bash\">kubectl create deployment web-deploy --image nginx --port 80 --replicas 2<\/code><\/pre>\n<p>Create a service for this deployment <\/p>\n<pre><code class=\"language-bash\">kubectl expose deployment web-deploy --name web-svc --port 80 --target-port 80 --type ClusterIP<\/code><\/pre>\n<p>Once both deployment and service creation are completed, we can check them using the following command.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get deploy,svc\n\nNAME                         READY   UP-TO-DATE   AVAILABLE   AGE\ndeployment.apps\/web-deploy   2\/2     2            2           2m10s\n\nNAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE\nservice\/web-svc      ClusterIP   10.100.235.119   &lt;none&gt;        80\/TCP    54s<\/code><\/pre>\n<p>Now that our demo application is ready, our next task is to setup external traffic to this application using the envoy gateway.<\/p>\n<p>Let&#8217;s start with creating the Envoy Proxy Custom Resource.<\/p>\n<h2 id=\"create-envoyproxy-resource\">Create EnvoyProxy Resource<\/h2>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">By default, <b><strong style=\"white-space: pre-wrap;\">Envoy Data Planes<\/strong><\/b> use a <b><code spellcheck=\"false\" style=\"white-space: pre-wrap;\"><strong>LoadBalancer<\/strong><\/code><\/b> service type for Data Planes. This means that if you are running in a cloud cluster, such as <a href=\"https:\/\/devopscube.com\/create-aws-eks-cluster-eksctl\/\" rel=\"noreferrer\">EKS<\/a> or AKS, the Load Balancer will be automatically created when you deploy the <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">gateway<\/code> CRD.<\/p>\n<p>Since this is a test setup, we do not want a LoadBalancer. So we can change the Data Plane configuration to use <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">NodePort<\/code> using the <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">EnvoyProxy<\/code> CRD. <\/p>\n<p>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.<\/p><\/div>\n<\/div>\n<blockquote><p><strong>Note:<\/strong> Skip the following EnvoyProxy modification if you can can use a Load Balancer to test the external traffic.<\/p><\/blockquote>\n<p>Create the <code>EnvoyProxy<\/code> manifest with the parameter of <code>type: NodePort<\/code>. This will overwrite the default configuration available on the Control Plane.<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt;EOF&gt; envoyproxy.yaml\napiVersion: gateway.envoyproxy.io\/v1alpha1\nkind: EnvoyProxy\nmetadata:\n  name: envoy-proxy\n  namespace: default\nspec:\n  provider:\n    type: Kubernetes\n    kubernetes:\n      envoyService:\n        type: NodePort\nEOF<\/code><\/pre>\n<p>Apply the Envoy Proxy on the Kubernetes cluster.<\/p>\n<pre><code class=\"language-bash\">kubectl apply -f envoyproxy.yaml<\/code><\/pre>\n<p>List the EnvoyProxy resource once the installation is completed.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get envoyproxy\n\nNAME          AGE\nenvoy-proxy   22m<\/code><\/pre>\n<p>The first step is successfully completed, so we can now create the <code>GatewayClass<\/code> resource and attach this Envoy Proxy.<\/p>\n<h2 id=\"create-gatewayclass-resource\">Create GatewayClass Resource <\/h2>\n<p>A cluster can have multiple gateway API controllers. The <strong><code>GatewayClass<\/code> <\/strong>Custom Resource is used to identify which gateway API controller to be used. <\/p>\n<p>So in the GatewayClass manifest, we need to mention the controller ID on the <code>spec.controllerName<\/code> parameter as well as the created EnvoyProxy under the <code>spec.parametersRef<\/code> section.<\/p>\n<p>Execute the following to create <code>gatewayclass.yaml<\/code><\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt;EOF&gt; gatewayclass.yaml\napiVersion: gateway.networking.k8s.io\/v1\nkind: GatewayClass\nmetadata:\n  name: envoy-gateway-class\nspec:\n  controllerName: gateway.envoyproxy.io\/gatewayclass-controller\n  parametersRef:\n    group: gateway.envoyproxy.io\n    kind: EnvoyProxy\n    name: envoy-proxy\n    namespace: default\nEOF<\/code><\/pre>\n<p>The important config to note here is, <code>gateway.envoyproxy.io\/gatewayclass-controller<\/code> 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.<\/p>\n<blockquote><p><strong>Note<\/strong>: Skip the <code>parametersRef<\/code> section, if you don&#8217;t want to use the NodePort service for the Data Planes.<\/p><\/blockquote>\n<p>Apply the Gateway Class configuration.<\/p>\n<pre><code class=\"language-bash\">kubectl apply -f gatewayclass.yaml<\/code><\/pre>\n<p>To check the resource creation.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get gatewayclass\n\nNAME                  CONTROLLER                                      \nenvoy-gateway-class   gateway.envoyproxy.io\/gatewayclass-controller <\/code><\/pre>\n<p>The Gateway Class resource creation is now completed. We can now create the <code>Gateway<\/code> Custom Resource.<\/p>\n<h2 id=\"create-gateway-resource\">Create Gateway Resource <\/h2>\n<p>Gateway Custom Resource is like a router. When the traffic enters the cluster, it initially hits the Gateway.<\/p>\n<p>When you create a Gateway resource, the Control Plane controller essentially <strong>deploys an Envoy Proxy pod<\/strong> (Data Planes). So all the routing logic will be part of this proxy.<\/p>\n<p>Lets create a <code>Gateway<\/code> manifest for the HTTP protocol, with Port 80 traffic to enter.<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt;EOF&gt; gateway.yaml\napiVersion: gateway.networking.k8s.io\/v1\nkind: Gateway\nmetadata:\n  name: web-gateway\n  namespace: default\nspec:\n  gatewayClassName: envoy-gateway-class\n  listeners:\n    - name: http\n      protocol: HTTP\n      port: 80\n      allowedRoutes:\n        namespaces:\n          from: All\nEOF<\/code><\/pre>\n<p>As you can see in the manifest, <strong>Gateways are listener-specific<\/strong>. This means you must clearly define which port and protocol the incoming traffic should use.<\/p>\n<p>Now, apply the Gateway to the cluster.<\/p>\n<pre><code class=\"language-bash\">kubectl apply -f gateway.yaml<\/code><\/pre>\n<p>To check the Gateway status, use the following command.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n default get gateway\n\nNAME          CLASS                 ADDRESS      PROGRAMMED   AGE\nweb-gateway   envoy-gateway-class   172.30.1.2   True         2m10s<\/code><\/pre>\n<p>Due to the modified Envoy proxy configs, the Data Plane will be deployed with the type <code>NodePort<\/code> service type. We can check it using the following command.<\/p>\n<pre><code class=\"language-bash\">kubectl -n envoy-gateway-system get deploy,svc\n<\/code><\/pre>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/07\/image-110.png\" class=\"kg-image\" alt=\"The output of the deployed envoy gateway pod with NodePort \" loading=\"lazy\" width=\"1666\" height=\"592\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/07\/image-110.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/07\/image-110.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/07\/image-110.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/07\/image-110.png 1666w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p><strong>Note down your NodePort number<\/strong> (<code>31299<\/code>) to test the application access in the upcoming section.<\/p>\n<p>Now the Gateway is ready, but we need to create one more resource called an <code>HTTPRoute<\/code>.<\/p>\n<h2 id=\"create-a-httproute-resource\">Create a HTTPRoute Resource<\/h2>\n<p>The <code>HTTPRoute<\/code> Custom Resource will help the traffic from the Gateway to the appropriate service of the application.<\/p>\n<p>We need to create a HTTPRoute manifest with the following contents.<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt;EOF&gt; httproute.yaml\napiVersion: gateway.networking.k8s.io\/v1\nkind: HTTPRoute\nmetadata:\n  name: web-httproute\nspec:\n  parentRefs:\n    - name: web-gateway\n  rules:\n    - backendRefs:\n        - group: \"\"\n          kind: Service\n          name: web-svc\n          port: 80\n          weight: 1\n      matches:\n        - path:\n            type: PathPrefix\n            value: \/\nEOF<\/code><\/pre>\n<p>Apply the HTTPRoute manifest.<\/p>\n<pre><code class=\"language-bash\">kubectl apply -f httproute.yaml<\/code><\/pre>\n<p>Once it is deployed, we can list the available HTTPRoute resources.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n default get httproute\n\nNAME            HOSTNAMES   AGE\nweb-httproute               7m23s<\/code><\/pre>\n<p>We have configured everything needed to route external traffic to our application. Next is testing the traffic.<\/p>\n<h2 id=\"test-the-application-traffic\">Test the Application Traffic<\/h2>\n<p>We can now try to access our application using the <code>curl<\/code> command from the local machine. For that, we need the <code>NodePort<\/code> of Gateway proxy and the <a href=\"https:\/\/devopscube.com\/ip-address-tutorial\/\" rel=\"noreferrer\">IP<\/a> of one of the worker nodes.<\/p>\n<p>Use the following command to see the output.<\/p>\n<pre><code class=\"language-bash\">curl [NODE_IP]:[NODE_PORT_NUMBER]<\/code><\/pre>\n<p>The output will show the Nginx web page.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/07\/image-111.png\" class=\"kg-image\" alt=\"the output of the demo application\" loading=\"lazy\" width=\"1896\" height=\"1650\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/07\/image-111.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/07\/image-111.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/07\/image-111.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/07\/image-111.png 1896w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>This ensures that the traffic is properly routed to the correct backend service through the envoy proxy.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">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.<\/div>\n<\/div>\n<h2 id=\"advantages-of-envoy-gateway\">Advantages of Envoy Gateway<\/h2>\n<p>We already have many <a href=\"https:\/\/gateway-api.sigs.k8s.io\/implementations\/?ref=devopscube.com\" rel=\"noreferrer\">Gateway API controllers<\/a>, so why use Envoy Gateway?<\/p>\n<p>The following are some of the advantages.<\/p>\n<ol>\n<li>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.<\/li>\n<li>Envoy proxy uses a <strong>filter chain<\/strong> to handle HTTP processing, rate limiting, and authentication. For this, it uses <strong>WebAssembly (WASM),<\/strong> so we can also create custom filters.<\/li>\n<li>Popular services mesh like <a href=\"https:\/\/devopscube.com\/istio-opensource-platform-microservices-management\/\" rel=\"noreferrer\"><strong>Istio<\/strong><\/a> use Envoy proxies, so using Envoy Gateway would be easier for the integration between them.<\/li>\n<\/ol>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>We saw how Envoy Gateway routes external traffic to backend services using the Gateway API.<\/p>\n<p>But Envoy Gateway can do much more using its custom resources. <\/p>\n<p>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.<\/p>\n<p>Over to you.<\/p>\n<p>Are you planning to implement Envoy gateway for your projects?<\/p>\n<p>Are you using any other controller now?<\/p>\n<p>Either way, let us know your thoughts in the comments.<\/p>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/setup-envoy-gateway-api\/\" target=\"_blank\" rel=\"noopener noreferrer\">Setup Envoy Gateway API Controller On Kubernetes (Guide) \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/setup-envoy-gateway-api\/<\/p>\n","protected":false},"author":1,"featured_media":358,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-357","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/357","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=357"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/357\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/358"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=357"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=357"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=357"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}