{"id":295,"date":"2026-04-14T01:16:00","date_gmt":"2026-04-14T01:16:00","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=295"},"modified":"2026-04-14T01:16:00","modified_gmt":"2026-04-14T01:16:00","slug":"kubernetes-gateway-api","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=295","title":{"rendered":"Kubernetes Gateway API Tutorial for Beginner&#8217;s"},"content":{"rendered":"<p>In this blog, you will learn about the Kubernetes Gateway API and its setup and configuration in a Kubernetes cluster.<\/p>\n<p>By the end of this guide, you will have learned:<\/p>\n<ul>\n<li>What the Kubernetes Gateway API is<\/li>\n<li>Key concepts of the Gateway API<\/li>\n<li>Practical implementation of Gateway API controllers<\/li>\n<li>How to use Gateway API objects like <code>GatewayClass<\/code>, <code>Gateway<\/code>, and <code>HTTPRoute<\/code><\/li>\n<li>How to implement path-based routing using the Gateway API and more.<\/li>\n<\/ul>\n<p>Let&#8217;s get started.<\/p>\n<h2 id=\"what-is-kubernetes-gateway-api\">What is Kubernetes Gateway API?<\/h2>\n<p>As the name suggests, the Gateway API is a Kubernetes feature that helps&nbsp;<strong>create gateways for external traffic<\/strong>&nbsp;entering your cluster.<\/p>\n<p><em>But if Ingress already does that, why do we need something new?<\/em><\/p>\n<p><strong>Ingress<\/strong> is the traffic routing mechanism primarily used in Kubernetes environments. However, it comes with several limitations. For example,<\/p>\n<ul>\n<li>It supports only host and path-based routing for HTTP\/HTTPS (Layer 7).<\/li>\n<li>Cross-namespace routing is complex and not straightforward.<\/li>\n<li>Relies heavily on annotations for anything beyond basic routing<\/li>\n<li>and more..<\/li>\n<\/ul>\n<p>So, to overcome all the Ingress limitations, the Kubernetes Gateway API was developed.<\/p>\n<p>The Gateway API has the following key features.<\/p>\n<ol>\n<li>Gateway API can perform <strong>L4 and L7 routing<\/strong> based on HTTP, gRPC, or TCP\/UDP (experimental)<\/li>\n<li>Can route traffic based on HTTP headers.<\/li>\n<li>Can perform <strong>cross-namespace routing.<\/strong><\/li>\n<li>Supports weighted traffic routing, blue-green deployments, canary deployments, and more.<\/li>\n<li>Reduced reliance on vendor-specific controller annotations, making configurations more portable across environments.<\/li>\n<li>Gateway API also works well with <a href=\"https:\/\/devopscube.com\/service-mesh-tools\/\" rel=\"noreferrer noopener\">service mesh<\/a> integrations such as <a href=\"https:\/\/devopscube.com\/istio-ingress-kubernetes-gateway-api\/\" rel=\"noreferrer\">Istio<\/a>, Linkerd, etc.<\/li>\n<\/ol>\n<p>In summary, the Gateway API is an improved version of Kubernetes Ingress that offers more powerful and flexible traffic management.<\/p>\n<p>So if you already have a solid understanding of Kubernetes Ingress, Ingress Controllers, and their limitations, picking up the Gateway API concepts is easier.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udcda<\/div>\n<div class=\"kg-callout-text\">If you are looking for a detailed guide to setup Istio on a Kubernetes Cluster, then here is a <a href=\"https:\/\/devopscube.com\/setup-istio-on-kubernetes\/\">detailed guide<\/a>.<\/div>\n<\/div>\n<h2 id=\"kubernetes-gateway-concepts\">Kubernetes Gateway Concepts<\/h2>\n<p>Before we get into the hands-on section, let&#8217;s understand the key concepts behind the Gateway API.<\/p>\n<h3 id=\"gateway-api-resources\">Gateway API Resources<\/h3>\n<p>The following are the key native resources that are part of the Kubernetes Gateway API.<\/p>\n<ol>\n<li><code><strong>GatewayClass<\/strong><\/code> (cluster scoped) : This resource&nbsp;is used to select which controller (Nginx, Envoy, Istio etc) will manage the Gateway resources. It is similar to <code>ingressClass<\/code> in <a href=\"https:\/\/devopscube.com\/kubernetes-ingress-tutorial\/\" rel=\"noreferrer\">Kubernetes ingress<\/a>.<\/li>\n<li><strong><code>Gateway<\/code><\/strong> (namespace scoped): The Gateway resource defines how external traffic enters the cluster. Creating a Gateway triggers the controller to provision a real cloud Load Balancer<\/li>\n<li><strong><code>HTTPRoute<\/code><\/strong> (namespace scoped): The&nbsp;HTTPRoute&nbsp;resource defines how traffic should be routed to applications inside the cluster once it reaches the Gateway for HTTP traffic. <\/li>\n<li><strong><code>gRPCRoute<\/code><\/strong>(namespace scoped): Resource to manage the gRPC traffic.<\/li>\n<li><strong><code>ReferenceGrants<\/code><\/strong> (namespace scoped): This resource is used primarily for enabling cross namespace routing.<\/li>\n<\/ol>\n<p>These objects by themselves cannot route or implement traffic routing in the cluster. The objects mentioned above mainly <strong>hold the configuration for how traffic should be routed. <\/strong>So you can call it the config layer.<\/p>\n<p>To actually route traffic, we need a Gateway API controller implementation.<\/p>\n<h3 id=\"gateway-api-controller\">Gateway API Controller<\/h3>\n<p>In Ingress controllers, we define routing rules in the <strong>Ingress object<\/strong>. The <strong>Ingress Controller<\/strong> handles the actual routing.<\/p>\n<p>The same concept applies to the Gateway API.<\/p>\n<p>While the Gateway API provides many objects to manage cluster traffic, the actual routing is done by a&nbsp;<strong>Gateway API Controller<\/strong>. This controller is not built into Kubernetes. You need to set up a third-party (vendor) controller, just like with Ingress.<\/p>\n<p>Various Gateway API controllers are available. The following are some of the popular ones.<\/p>\n<ol>\n<li><a href=\"https:\/\/docs.nginx.com\/nginx-gateway-fabric\/?ref=devopscube.com\" rel=\"noreferrer\">Nginx Gateway Fabric<\/a><\/li>\n<li><a href=\"https:\/\/developer.konghq.com\/kubernetes-ingress-controller\/gateway-api\/?ref=devopscube.com\" rel=\"noreferrer\">Kong Gateway<\/a><\/li>\n<li><a href=\"https:\/\/devopscube.com\/setup-envoy-gateway-api\/\" rel=\"noreferrer\">Envoy Gateway<\/a><\/li>\n<li><a href=\"https:\/\/doc.traefik.io\/traefik\/reference\/install-configuration\/providers\/kubernetes\/kubernetes-gateway\/?ref=devopscube.com\" rel=\"noreferrer\">Traefik Gateway<\/a><\/li>\n<\/ol>\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 this guide, we we have used the Nginx Gateway Fabric controller as a example. The core workflow remains that same for all the controllers.<\/div>\n<\/div>\n<h2 id=\"nginx-gateway-fabric-architecture\">Nginx Gateway Fabric Architecture<\/h2>\n<p>To understand Gateway API concepts better, <strong>you need to understand the controller architecture<\/strong>. So we will use NGINX Gateway Fabric to understand how a controller implementation works with Gateway API.<\/p>\n<p>The Nginx Gateway Fabric has the following two components.<\/p>\n<ol>\n<li>Control Plane and<\/li>\n<li>Data Plane<\/li>\n<\/ol>\n<p>The following image illustrates the architecture of the Nginx Gateway Fabric controller.<\/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\/04\/image-41.png\" class=\"kg-image\" alt=\"architecture of the Nginx Gateway Fabric controller used for gateway API\" loading=\"lazy\" width=\"1718\" height=\"1665\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2026\/04\/image-41.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2026\/04\/image-41.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2026\/04\/image-41.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2026\/04\/image-41.png 1718w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<h3 id=\"control-plane-handles-config\">Control Plane (Handles Config)<\/h3>\n<p>The control plane runs as a pod called the fabric controller. It&#8217;s a&nbsp;<strong>Kubernetes controller<\/strong>&nbsp;that watches Gateway API resources. When you define routing rules, the controller reads them and converts them into NGINX config files.<\/p>\n<p>But the controller&nbsp;<strong>does not handle real traffic<\/strong>. It doesn&#8217;t run NGINX itself.<\/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\">In a normal ingress implemenation, the controller itself will handle the traffic.<\/div>\n<\/div>\n<h3 id=\"data-plane-proxy-layer\">Data Plane (Proxy Layer)<\/h3>\n<p>The data plane handles the actual traffic. It&#8217;s a separate NGINX proxy pod that&nbsp;<strong>gets created automatically when you create a Gateway<\/strong>&nbsp;resource. This pod runs NGINX along with an NGINX agent.<\/p>\n<p>A Kubernetes Service is also created for the NGINX pod. This service is exposed to the outside world using a LoadBalancer or NodePort, so client requests can reach your app.<\/p>\n<p>Whenever routing rules change, <strong>the fabric controller sends<\/strong> the updated NGINX config to the agent using gRPC. The agent then applies the new config to the NGINX pod to route traffic correctly.<\/p>\n<p>Now that we have looked in to key  Gateway API concepts, lets understand how everything works together to handle the traffic.<\/p>\n<h2 id=\"gateway-api-traffic-flow\">Gateway API Traffic Flow<\/h2>\n<p>The following image shows how the traffic is routed to the cluster pods from outside world through the Kubernetes Gateway API resources. It&#8217;s meant to give you a high-level overview of how everything is connected<\/p>\n<figure class=\"kg-card kg-image-card kg-card-hascaption\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2026\/04\/image-63.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"2000\" height=\"2312\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2026\/04\/image-63.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2026\/04\/image-63.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2026\/04\/image-63.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w2400\/2026\/04\/image-63.png 2400w\" sizes=\"auto, (min-width: 720px) 720px\"><figcaption><span style=\"white-space: pre-wrap;\">Image illustrates the relationship between the Gateway API resources, controller and the proxy pod that actually handles the traffic<\/span><\/figcaption><\/figure>\n<p>Here is how it works.<\/p>\n<ol>\n<li>When we install the Gateway API Controller, it only deploys the Control Plane Controller.<\/li>\n<li>When we create a <strong>Gateway Custom Resource<\/strong> configuration for our application, the Control Plane controller (Nginx Fabric) creates a Data Plane (Proxy Pod &#8211; Nginx agent) and a Load Balancer for it.<\/li>\n<li>All the configurations from the <strong>Gateway<\/strong> and <strong>HTTPRoute<\/strong> custom resources are converted into routing rules inside proxy pod..<\/li>\n<li>When someone tries to access the application, the request goes through the external Load Balancer, then reaches the Gateway API Data Plane (Proxy pod).<\/li>\n<li>The proxy pod will then send the traffic to the correct backend services based on rules.<\/li>\n<\/ol>\n<p>Now that you have an understanding of how Gateway API works, lets do the hands on exercise. You will be able to relate better to all the concepts we learned so far when we go through the practical steps.<\/p>\n<h2 id=\"setup-prerequisites\">Setup Prerequisites<\/h2>\n<p>The following are the requirements for the setup<\/p>\n<ol>\n<li><a href=\"https:\/\/devopscube.com\/upgrade-kubernetes-cluster-kubeadm\/\" rel=\"noreferrer\">Kubernetes Cluster<\/a> v1.30 or higher.<\/li>\n<li><a href=\"https:\/\/devopscube.com\/install-configure-helm-kubernetes\/\" rel=\"noreferrer\">Helm<\/a> [Local Machine]<\/li>\n<li><a href=\"https:\/\/devopscube.com\/kubectl-set-context\/\" rel=\"noreferrer\">Kubectl<\/a> [Local Machine]<\/li>\n<\/ol>\n<p>We can begin the installation from the Gateway API.<\/p>\n<h2 id=\"setup-gateway-api-in-the-kubernetes-cluster\">Setup Gateway API in the Kubernetes Cluster<\/h2>\n<p>The Gateway API setup consists of three important section.<\/p>\n<ol>\n<li>Gateway API CRD installation<\/li>\n<li>Gateway API controller installation<\/li>\n<li>Gateway API object creation and traffic validation.<\/li>\n<\/ol>\n<p>We will look at all the steps in detail.<\/p>\n<h3 id=\"step-1-install-gateway-api-crds\">Step 1: Install Gateway API CRDs<\/h3>\n<p>The Gateway API objects are not available as native objects in Kubernetes. We need to enable them by installing the Gateway API Custom Resource Definitions (CRDs)<\/p>\n<p>To install the Gateway API CRDs, use the following command. To get the latest version of the CRD, please visit <a href=\"https:\/\/gateway-api.sigs.k8s.io\/guides\/?ref=devopscube.com#install-standard-channel\" rel=\"noreferrer\">this page<\/a>. At the time of updating this guide, v1.3.0 is the latest version.<\/p>\n<pre><code class=\"language-bash\">kubectl apply -f https:\/\/github.com\/kubernetes-sigs\/gateway-api\/releases\/download\/v1.3.0\/standard-install.yaml<\/code><\/pre>\n<p>Now, validate the installed CRDs using the following command.&nbsp;You should see the five CRD resources we discussed earlier.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get crds | grep gateway\n\ngatewayclasses.gateway.networking.k8s.io     2025-05-21T13:49:23Z\ngateways.gateway.networking.k8s.io           2025-05-21T13:49:29Z\ngrpcroutes.gateway.networking.k8s.io         2025-05-21T13:49:32Z\nhttproutes.gateway.networking.k8s.io         2025-05-21T13:49:35Z\nreferencegrants.gateway.networking.k8s.io    2025-05-21T13:49:36Z<\/code><\/pre>\n<p>You can also check the API resources to get more details about the registered CRDs.<\/p>\n<pre><code class=\"language-bash\">$ kubectl api-resources --api-group=gateway.networking.k8s.io\n\nNAME              SHORTNAMES   APIVERSION                          NAMESPACED   KIND\ngatewayclasses    gc           gateway.networking.k8s.io\/v1        false        GatewayClass\ngateways          gtw          gateway.networking.k8s.io\/v1        true         Gateway\ngrpcroutes                     gateway.networking.k8s.io\/v1        true         GRPCRoute\nhttproutes                     gateway.networking.k8s.io\/v1        true         HTTPRoute\nreferencegrants   refgrant     gateway.networking.k8s.io\/v1beta1   true         ReferenceGrant<\/code><\/pre>\n<h3 id=\"step-2-install-gateway-api-controller\">Step 2: Install Gateway API Controller<\/h3>\n<p>There are various controllers that support the Gateway API, you can refer to the supported controllers list in the <a href=\"https:\/\/gateway-api.sigs.k8s.io\/implementations\/?ref=devopscube.com\">official documentation<\/a>.<\/p>\n<p>For this tutorial, we will be using the <strong>NGINX Gateway Fabric Controller<\/strong>, which is now generally available (GA).<\/p>\n<p>We&#8217;ll deploy the controller using Helm. To pull the <a href=\"https:\/\/devopscube.com\/create-helm-chart\/\" rel=\"noreferrer\">Helm chart<\/a>, run the following command:<\/p>\n<pre><code class=\"language-bash\">git clone https:\/\/github.com\/techiescamp\/cka-certification-guide.git\n\ncd cka-certification-guide\/helm-charts\/nginx-gateway-fabric\/<\/code><\/pre>\n<p>This will give the local copy of the Helm chart so we can modify and store it in version control systems like GitHub.<\/p>\n<p>The modifiable values will be available in the <code>values.yaml<\/code> file, and we can make changes in this file as per our requirements.<\/p>\n<p>The following snapshot is the directory structure of the <strong>Nginx Gateway Fabric Controller<\/strong> Helm Chart.<\/p>\n<pre><code class=\"language-bash\">controlplane:~\/cka-certification-guide\/helm-charts\/nginx-gateway-fabric$ tree\n.\n|-- Chart.yaml\n|-- README.md\n|-- crds\n|   |-- gateway.nginx.org_clientsettingspolicies.yaml\n|   |-- gateway.nginx.org_nginxgateways.yaml\n|   |-- gateway.nginx.org_nginxproxies.yaml\n|   |-- gateway.nginx.org_observabilitypolicies.yaml\n|   |-- gateway.nginx.org_snippetsfilters.yaml\n|   `-- gateway.nginx.org_upstreamsettingspolicies.yaml\n|-- custom-values.yaml\n|-- templates\n|   |-- _helpers.tpl\n|   |-- certs-job.yaml\n|   |-- clusterrole.yaml\n|   |-- clusterrolebinding.yaml\n|   |-- deployment.yaml\n|   |-- nginxgateway.yaml\n|   |-- nginxproxy.yaml\n|   |-- scc.yaml\n|   |-- service.yaml\n|   `-- serviceaccount.yaml\n|-- values.schema.json\n`-- values.yaml<\/code><\/pre>\n<blockquote><p>Note: The <code>custom-values.yaml<\/code>file we have created for the Gateway API customization, that we explain in the NodePort section.<\/p><\/blockquote>\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\">In the controller Helm chart, the default service type <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">values.yaml<\/code> is set to <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">LoadBalancer<\/code>. This means that when you deploy it in a cloud environment, it will automatically provision a LoadBalancer.<\/p>\n<p>If you&#8217;re not deploying in a cloud environment, you&#8217;ll need to expose the controller service using a <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">NodePort<\/code> instead. Refer to the <a href=\"https:\/\/devopscube.com\/kubernetes-gateway-api\/#expose-gateway-api-controller-as-node-port-service\" rel=\"noreferrer\">NodePort section<\/a> for instructions on how to deploy the controller with this configuration.<\/div>\n<\/div>\n<p>Following are the <a href=\"https:\/\/devopscube.com\/build-docker-image\/\" rel=\"noreferrer\">container images<\/a> the controller chart uses.<\/p>\n<ol>\n<li><code>ghcr.io\/nginxinc\/nginx-gateway-fabric:1.5.1<\/code><\/li>\n<li><code>ghcr.io\/nginxinc\/nginx-gateway-fabric\/nginx:1.5.1<\/code><\/li>\n<\/ol>\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\">If you are working on a corporate network, you might not be able to access public images. In that case, you should first push the required images to your organization&#8217;s private registry, then update the image references in the chart before deploying it.<\/p>\n<p>Also, make sure to review your company&#8217;s security policies to confirm whether pushing community images to private registries is permitted.<\/p><\/div>\n<\/div>\n<p>We&#8217;re not making any changes to the controller configuration at this stage. <\/p>\n<p>We&#8217;ll go ahead and install the NGINX Gateway Fabric Controller (Control Plane) directly using the following command.<\/p>\n<pre><code class=\"language-bash\">$ helm install ngf . -n nginx-gateway --create-namespace<\/code><\/pre>\n<p>Once the installation is complete, make sure all the controller components are running correctly by using the following command.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n nginx-gateway get all\n\nNAME                                           READY   STATUS    RESTARTS   AGE\npod\/ngf-nginx-gateway-fabric-85f857f4b-p47c4   1\/1     Running   0          31s\n\nNAME                               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE\nservice\/ngf-nginx-gateway-fabric   ClusterIP   10.100.247.89   &lt;none&gt;        443\/TCP   32s\n\nNAME                                       READY   UP-TO-DATE   AVAILABLE   AGE\ndeployment.apps\/ngf-nginx-gateway-fabric   1\/1     1            1           32s\n\nNAME                                                 DESIRED   CURRENT   READY   AGE\nreplicaset.apps\/ngf-nginx-gateway-fabric-85f857f4b   1         1         1       32s<\/code><\/pre>\n<p>As you can see, the control plane controller pods are up and running without any issues. <\/p>\n<p>During this installation, the Nginx Fabric related Custom Resource Definitions also be installed.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get crds | grep -iE \"gateway.nginx\"\n\nclientsettingspolicies.gateway.nginx.org     2025-07-02T05:20:35Z\nnginxgateways.gateway.nginx.org              2025-07-02T05:20:35Z\nnginxproxies.gateway.nginx.org               2025-07-02T05:20:37Z\nobservabilitypolicies.gateway.nginx.org      2025-07-02T05:20:37Z\nsnippetsfilters.gateway.nginx.org            2025-07-02T05:20:38Z\nupstreamsettingspolicies.gateway.nginx.org   2025-07-02T05:20:38Z<\/code><\/pre>\n<p>These Nginx Fabric CRDs are used to customize the controller configurations.<\/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\">By default, the controller is installed with a service of type <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">LoadBalancer<\/code>. <\/p>\n<p>In AWS, this means the Gateway API Controller will provision a Classic <a href=\"https:\/\/devopscube.com\/aws-load-balancers\/\" rel=\"noreferrer\">Load Balancer<\/a> to handle incoming traffic. <\/p>\n<p>In Azure, it creates a Standard Public Load Balancer. <\/p>\n<p>In GCP, it provisions a Google Cloud External Load Balancer to route traffic to the backend pods.<\/p><\/div>\n<\/div>\n<h3 id=\"step-3-create-gateway-class\">Step 3: Create Gateway Class<\/h3>\n<p>Gateway Class is a Custom Resource of the Gateway API (<code>gatewayclasses.gateway.networking.k8s.io<\/code>).<\/p>\n<p>This will map the <strong>identity<\/strong> of the Control Plane controller to a configuration file. This helps the Gateway object to choose which Gateway API controller to use.<\/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\">Some Gateway API deployments come with the GatewayClass by default. If the <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">GatewayClass<\/code> isn&#8217;t included in the controller Helm chart, you&#8217;ll need to create it manually.<\/div>\n<\/div>\n<p>Create a <code>GatewayClass<\/code> manifest named <code>gateway.yaml<\/code> and add the following contents.<\/p>\n<pre><code class=\"language-yaml\">apiVersion: gateway.networking.k8s.io\/v1\nkind: GatewayClass\nmetadata:\n  name: nginx-gateway-class\nspec:\n  controllerName: gateway.nginx.org\/nginx-gateway-controller\n  parametersRef:\n    group: gateway.nginx.org\n    kind: NginxProxy\n    name: ngf-proxy-config\n    namespace: nginx-gateway<\/code><\/pre>\n<ul>\n<li><code>gateway.nginx.org\/nginx-gateway-controller<\/code> &#8211; The identity of the Nginx Fabric Gateway API Controller.<\/li>\n<\/ul>\n<p>Deploy the Gateway Class manifest<\/p>\n<pre><code class=\"language-bash\">$ kubectl apply -f gateway.yaml<\/code><\/pre>\n<p>To see the available Gateway Class<\/p>\n<pre><code class=\"language-bash\">$ kubectl get gatewayclasses\n\nNAME                  CONTROLLER                                   ACCEPTED   AGE\nnginx-gateway-class   gateway.nginx.org\/nginx-gateway-controller   True       58s<\/code><\/pre>\n<p>We can describe the <strong><code>GatewayClass<\/code><\/strong> to get the detailed information.<\/p>\n<pre><code class=\"language-bash\">kubectl describe gatewayclass nginx-gateway-class<\/code><\/pre>\n<p>When you describe the <code>GatewayClass<\/code>, you&#8217;ll see details like its status, controller name, and other relevant information.<\/p>\n<p>Before creating the <code>Gateway<\/code> object, make sure to <strong>note down the <code>GatewayClass<\/code> <\/strong>name, as you&#8217;ll need it for the <code>Gateway<\/code> configuration.<\/p>\n<p>Now, let&#8217;s start with a simple application to see how traffic is routed through the Gateway API.<\/p>\n<h2 id=\"validate-gateway-api-traffic-flow\">Validate Gateway API Traffic Flow <\/h2>\n<p>The following image illustrates how the traffic from DNS to pod traffic flow using Gateway API implementation.<\/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\/04\/image-43.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"2000\" height=\"2480\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2026\/04\/image-43.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2026\/04\/image-43.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2026\/04\/image-43.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2026\/04\/image-43.png 2024w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Lets implement a similar workflow.<\/p>\n<h3 id=\"deploy-a-demo-application\">Deploy a Demo Application<\/h3>\n<p>To test the Gateway API implementation, we will Deploy an Nginx webserver and expose it as a <code>ClusterIP<\/code> service.<\/p>\n<pre><code class=\"language-yaml\">cat &lt;&lt;EOF &gt; nginx-deploy.yaml\napiVersion: v1\nkind: Namespace\nmetadata:\n  name: webserver\n---\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: nginx\n  namespace: webserver\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: nginx\n  template:\n    metadata:\n      labels:\n        app: nginx\n    spec:\n      containers:\n      - name: nginx-container\n        image: nginx:1.21\n        ports:\n        - containerPort: 80\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: nginx-service\n  namespace: webserver\nspec:\n  selector:\n    app: nginx\n  ports:\n  - protocol: TCP\n    port: 80\n    targetPort: 80\n  type: ClusterIP\nEOF\n<\/code><\/pre>\n<p>To deploy this, use the following command.<\/p>\n<pre><code class=\"language-bash\">kubectl apply -f nginx-deploy.yaml<\/code><\/pre>\n<p>To get the deployed components status.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n webserver get all\n\nNAME                                                   READY   STATUS    RESTARTS   AGE\npod\/nginx-74f7c565c5-h5rhd                             1\/1     Running   0          61m\npod\/nginx-74f7c565c5-p5tqh                             1\/1     Running   0          61m\n\nNAME                                      TYPE           CLUSTER-IP       EXTERNAL-IP                                    \nservice\/nginx-service                     ClusterIP      10.100.247.161   &lt;none&gt;                                          \n\nNAME                                              READY   UP-TO-DATE   AVAILABLE   AGE\ndeployment.apps\/nginx                             2\/2     2            2           61m\n\nNAME                                                         DESIRED   CURRENT   READY   AGE\nreplicaset.apps\/nginx-74f7c565c5                             2         2         2       61m<\/code><\/pre>\n<p>This ensures that the deployed webserver is appropriately running and that the service is also created for the <a href=\"https:\/\/devopscube.com\/kubernetes-deployment-tutorial\/\" rel=\"noreferrer noopener\">deployment<\/a>.<\/p>\n<p>On the next section, we will create a Gateway with listeners (incomming traffic Port and Protocol)<\/p>\n<h3 id=\"create-gateway-object\">Create Gateway object<\/h3>\n<p>The Gateway acts as the entry point for traffic entering the cluster.<\/p>\n<p>When we create a Gateway, the Control Plane will create a Data Plane on the cluster.<\/p>\n<p>The Data plane is nothing but a Nginx server deployment with a service. <\/p>\n<p>The control plane translates the configuration from the Gateway manifest to the Nginx configuration.<\/p>\n<p>Meaning, once the traffic reaches the cluster, the <code>Gateway<\/code> determines how it should be handled based on the defined <strong>Listeners<\/strong><\/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\"><code spellcheck=\"false\" style=\"white-space: pre-wrap;\">Gateway<\/code> is a <b><strong style=\"white-space: pre-wrap;\">namespace-scoped<\/strong><\/b> object. So you can&#8217;t use the same <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">Gateway<\/code> across multiple namespaces.<\/div>\n<\/div>\n<p>Lets create a <code>Gateway<\/code> for the demo application. We are creating the <code>Gateway<\/code> in the <code>webserver<\/code> namespace<\/p>\n<pre><code class=\"language-yaml\">cat &lt;&lt;EOF &gt; web-gateway.yaml\napiVersion: gateway.networking.k8s.io\/v1\nkind: Gateway\nmetadata:\n  name: web-gateway\n  namespace: webserver\nspec:\n  gatewayClassName: nginx-gateway-class\n  listeners:\n    - name: http\n      protocol: HTTP\n      port: 80\nEOF<\/code><\/pre>\n<p>Key Parts in this YAML should know.<\/p>\n<ol>\n<li><code>gatewayClassName: nginx-gateway-class<\/code>: Associates this Gateway with the <code>GatewayClass<\/code> named <code>nginx-gateway-class<\/code><\/li>\n<\/ol>\n<p>Now, lets create the <code>Gateway<\/code><\/p>\n<pre><code class=\"language-bash\">kubectl apply -f web-gateway.yaml<\/code><\/pre>\n<p>To list the Gateway from <code>webserver<\/code> Namespace, use the following command.<\/p>\n<pre><code>kubectl -n webserver get gateway\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-19.png\" class=\"kg-image\" alt=\"listing the deployed gateway api gateway\" loading=\"lazy\" width=\"1577\" height=\"240\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/07\/image-19.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/07\/image-19.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/07\/image-19.png 1577w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Here, you can see that the Gateway is created a Load balancer because of the default Gateway API Helm configuration and we have deployed in AWS EKS.<\/p>\n<p>To get more detailed information, describe the Gateway<\/p>\n<pre><code class=\"language-bash\">kubectl -n webserver describe gateway web-gateway<\/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-25.png\" class=\"kg-image\" alt=\"describing the gateway to see the detailed information\" loading=\"lazy\" width=\"1395\" height=\"826\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/07\/image-25.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/07\/image-25.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/07\/image-25.png 1395w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Here, we can see that the <strong><code>GatewayClass<\/code><\/strong> is configured with the <strong><code>Gateway<\/code><\/strong> and also see the DNS name of the AWS Load Balancer.<\/p>\n<p>No routes are attached in the&nbsp;<strong>Listeners<\/strong>&nbsp;section, which we will configure in the next step.<\/p>\n<p>This indicates that we haven&#8217;t configured any services with the Gateway API.<\/p>\n<h3 id=\"create-a-http-route-object\">Create a HTTP Route Object <\/h3>\n<p><code>HTTPRoute<\/code> is the <strong>Custom Resource<\/strong> of the <strong>Gateway API<\/strong>, which contains the configurations to route the traffic to the HTTP\/HTTPS based applications.<\/p>\n<p>Since the demo application (<code>nginx<\/code>) is a webserver, which routes the HTTP\/HTTPS traffic, so that we are creating this resource.<\/p>\n<p>This Custom Resource can handle functions such as path-based routing, hostname-based routing, custom header routing, and namespace routing.<\/p>\n<pre><code class=\"language-yaml\">cat &lt;&lt;EOF &gt; webserver-httproute.yaml\napiVersion: gateway.networking.k8s.io\/v1\nkind: HTTPRoute\nmetadata:\n  name: webserver-httproute\n  namespace: webserver\nspec:\n  parentRefs:\n  - name: web-gateway\n  rules:\n  - matches:\n    - path:\n        type: PathPrefix\n        value: \/\n    backendRefs:\n    - name: nginx-service\n      port: 80\nEOF<\/code><\/pre>\n<pre><code class=\"language-bash\">kubectl apply -f webserver-httproute.yaml<\/code><\/pre>\n<p>To list the <code>HTTPRoute<\/code> custom resource, use the following command.<\/p>\n<pre><code class=\"language-bash\">kubectl -n webserver get httproute<\/code><\/pre>\n<p>Describe the <code>httproute<\/code> Custom Resource to get more detailed information.<\/p>\n<pre><code class=\"language-bash\">kubectl -n webserver describe httproute webserver-httproute<\/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-21.png\" class=\"kg-image\" alt=\"the detailed information of the gateway api http route custom resource\" loading=\"lazy\" width=\"873\" height=\"830\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/07\/image-21.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/07\/image-21.png 873w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>If the configurations are correctly done, we can see the Gateway API Controller, Gateway Class, and Gateway information along with the routing rules.<\/p>\n<p>Before we check our application, we must ensure that the Gateway Custom Resource is updated with the routes.<\/p>\n<pre><code class=\"language-bash\">kubectl -n webserver describe gateway web-gateway<\/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-26.png\" class=\"kg-image\" alt=\"the updated routed information in the gateway api gateway\" loading=\"lazy\" width=\"1394\" height=\"826\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/07\/image-26.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/07\/image-26.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/07\/image-26.png 1394w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>This clearly shows when we create a <code>HTTPRoute<\/code> Custom Resource, the Gateway resource will automatically be updated.<\/p>\n<p>Now, we can check our application over the browser.<\/p>\n<p>Paste the <strong>Load Balancer DNS<\/strong> name as a <strong>URL<\/strong> in any web browser.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/blog.techiescamp.com\/content\/images\/2025\/01\/image-74.png\" class=\"kg-image\" alt=\"The output of the application over the browser which is using the gateway api\" loading=\"lazy\" width=\"891\" height=\"409\"><\/figure>\n<p>If you check the Nginx Data Plane Pod configuration, you can see an upstream block with the details of the Pods IPs.<\/p>\n<pre><code class=\"language-bash\">k -n webserver exec -it &lt;CONTROLLER POD NAME&gt; -- nginx -T<\/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-23.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"808\" height=\"825\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/07\/image-23.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/07\/image-23.png 808w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>This is how the controller registers the Pods with the internal configuration and routes the traffic to that.<\/p>\n<h2 id=\"advanced-traffic-routing-in-gateway-api\">Advanced Traffic Routing in Gateway API<\/h2>\n<p>The <strong>Gateway API<\/strong> can do path-based, host-based, header, and method-based routing.<\/p>\n<p>Since this is an introduction to the Gateway API, we covers path and host-based routing.<\/p>\n<h3 id=\"step-1-create-a-gateway-custom-resource\">Step 1: Create a Gateway Custom Resource<\/h3>\n<p>Let&#8217;s start with the <strong>Gateway<\/strong> resource, this time also we are creating a Gateway with the same configurations that we have created for earlier demo.<\/p>\n<p>Before creating the Gateway, create a Namespace <code>colors<\/code><\/p>\n<pre><code class=\"language-bash\">kubectl create namespace colors<\/code><\/pre>\n<p>Create a Gateway in <code>colors<\/code> namespace.<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt;EOF &gt; colors-gateway.yaml\napiVersion: gateway.networking.k8s.io\/v1\nkind: Gateway\nmetadata:\n  name: colors-gateway\n  namespace: colors\nspec:\n  gatewayClassName: nginx-gateway-class\n  listeners:\n  - name: http\n    port: 80\n    protocol: HTTP\n    hostname: \"*.techiescamp.com\"\nEOF<\/code><\/pre>\n<p>Here, I have changed the name of the Gateway <code>colors-gateway<\/code> and Namespace <code>colors<\/code><\/p>\n<ol>\n<li><strong><code>hostname: \"*.techiescamp.com\"<\/code><\/strong>: This means, it will <strong>only accept traffic<\/strong> coming to domains like,\n<ol>\n<li>app.techiescamp.com<\/li>\n<li>api.techiescamp.com etc<\/li>\n<\/ol>\n<\/li>\n<\/ol>\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\">The <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">*<\/code> is a <b><strong style=\"white-space: pre-wrap;\">wildcard<\/strong><\/b>, so it matches anything before <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">.techiescamp.com.<\/code><\/p>\n<p>So, why use wildcard DNS? <\/p>\n<p>Because, when you have many services like <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">app<\/code>, <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">api<\/code>, <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">user<\/code>, etc, you want <b><strong style=\"white-space: pre-wrap;\">one Gateway<\/strong><\/b> to handle all of them without creating separate listeners<\/div>\n<\/div>\n<pre><code class=\"language-bash\">kubectl apply -f colors-gateway.yaml<\/code><\/pre>\n<p>After the deployment, if we list the Gateways, we can see that the provisioned Load Balancer by the Gateway Controller.<\/p>\n<pre><code class=\"language-bash\">kubectl -n colors get gateway<\/code><\/pre>\n<p>We can verify this from the AWS console as well.<\/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-28.png\" class=\"kg-image\" alt=\"checking the aws console to ensure that the load balancer is properly provisioned.\" loading=\"lazy\" width=\"1032\" height=\"544\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/07\/image-28.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/07\/image-28.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/07\/image-28.png 1032w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>We can use the <strong>DNS name<\/strong> of the <strong>Load Balancer<\/strong> to test our application, but that won&#8217;t be quite convenient.<\/p>\n<p>So configuring the <strong>Local DNS Resolution<\/strong> would make the work easier.<\/p>\n<p>To set the local DNS resolution, get the <strong>public IPs<\/strong> of the <strong>Load Balancer<\/strong>, choose an intended host name (Ex &#8211; <code>dev.techiescamp.com<\/code>) and configure it on <code>\/etc\/hosts<\/code>.<\/p>\n<p>Use the following command to get the public IPs of the Load Balancer.<\/p>\n<pre><code class=\"language-bash\">dig +short &lt;LOAD BALANCER DNS NAME&gt;<\/code><\/pre>\n<p>This will list the public IPs of the Load Balancer.<\/p>\n<p>Open <code>\/etc\/hosts<\/code> from your local machine and map the IP address with the hostname <code>dev.techiescamp.com<\/code>.<\/p>\n<pre><code>sudo vim \/etc\/hosts<\/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-29.png\" class=\"kg-image\" alt=\"local dns resolution for the load balancer provisioned by the gateway apli controller\" loading=\"lazy\" width=\"1366\" height=\"826\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/07\/image-29.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/07\/image-29.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/07\/image-29.png 1366w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>You can give any name instead of <code>dev.techiescamp.com<\/code> , and this is only resolved on your local machine.<\/p>\n<blockquote><p>Note: If anyone want to access the application over the internet with a domain name, you have to configure the Load Balancer DNS records with a DNS Server (Example: Route53).<\/p><\/blockquote>\n<h3 id=\"step-2-deploy-applications-for-demo\">Step 2: Deploy Applications for Demo<\/h3>\n<p>Deploying two demo applications in the same Namespace to explain the path based routing.<\/p>\n<pre><code class=\"language-yaml\">cat &lt;&lt;EOF &gt; colors-deployments.yaml\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: orange-app\n  namespace: colors\n  labels:\n    app: orange-app\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: orange-app\n  template:\n    metadata:\n      labels:\n        app: orange-app\n    spec:\n      containers:\n      - name: color-app\n        image: techiescamp\/go-color-app:latest\n        ports:\n        - containerPort: 8080\n        env:\n        - name: POD_NAME\n          valueFrom:\n            fieldRef:\n              fieldPath: metadata.name\n        - name: POD_IP\n          valueFrom:\n            fieldRef:\n              fieldPath: status.podIP\n\n---\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: green-app\n  namespace: colors\n  labels:\n    app: green-app\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: green-app\n  template:\n    metadata:\n      labels:\n        app: green-app\n    spec:\n      containers:\n      - name: color-app\n        image: techiescamp\/go-color-app:latest\n        ports:\n        - containerPort: 8080\n        env:\n        - name: POD_NAME\n          valueFrom:\n            fieldRef:\n              fieldPath: metadata.name\n        - name: POD_IP\n          valueFrom:\n            fieldRef:\n              fieldPath: status.podIP\nEOF\n<\/code><\/pre>\n<pre><code class=\"language-bash\">kubectl apply -f colors-deployments.yaml<\/code><\/pre>\n<p>Create <code>ClusterIP<\/code> services for these two applications.<\/p>\n<pre><code class=\"language-yaml\">cat &lt;&lt;EOF &gt; colors-svc.yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: orange-app-service\n  namespace: colors\n  labels:\n    app: orange-app\nspec:\n  selector:\n    app: orange-app\n  ports:\n  - protocol: TCP\n    port: 80\n    targetPort: 8080\n  type: ClusterIP\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: green-app-service\n  namespace: colors\n  labels:\n    app: green-app\nspec:\n  selector:\n    app: green-app\n  ports:\n  - protocol: TCP\n    port: 80\n    targetPort: 8080\n  type: ClusterIP\nEOF<\/code><\/pre>\n<pre><code class=\"language-bash\">kubectl apply -f colors-svc.yaml<\/code><\/pre>\n<p>Our demo applications are ready, now we can check the status and service list of the these two applications.<\/p>\n<pre><code class=\"language-bash\">kubectl -n colors get po,svc<\/code><\/pre>\n<p>This ensures that the application&#8217;s deployment and services are correctly done.<\/p>\n<h3 id=\"step-3-create-httproute-resource-for-applications\">Step 3: Create HTTPRoute Resource for Applications.<\/h3>\n<p>Till now, the external HTTP traffic can only reach the <strong>Gateway.<\/strong><\/p>\n<p>Now, we need to inform the Gateway to route the traffic to the <strong>Green<\/strong> and <strong>Orange<\/strong> applications.<\/p>\n<p>We can achieve this by creating a <code>HTTPRoute<\/code> resource with certain conditions.<\/p>\n<pre><code class=\"language-yaml\">cat &lt;&lt;EOF &gt; colors-http-route.yaml\napiVersion: gateway.networking.k8s.io\/v1\nkind: HTTPRoute\nmetadata:\n  name: colors-httproute\n  namespace: colors\nspec:\n  parentRefs:\n  - name: colors-gateway\n    sectionName: http\n  hostnames:\n  - \"dev.techiescamp.com\"\n  rules:\n  - matches:\n    - path:\n        type: PathPrefix\n        value: \/green\n    backendRefs:\n    - name: green-app-service\n      port: 80\n  - matches:\n    - path:\n        type: PathPrefix\n        value: \/orange\n    backendRefs:\n    - name: orange-app-service\n      port: 80\nEOF<\/code><\/pre>\n<pre><code class=\"language-bash\">kubectl apply -f colors-http-route.yaml<\/code><\/pre>\n<p>After the deployment, we must ensure all the routes are correctly configured.<\/p>\n<pre><code class=\"language-bash\">kubectl -n colors describe httproutes colors-httproute<\/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-30.png\" class=\"kg-image\" alt=\"the detailed information of the httproute custom resource\" loading=\"lazy\" width=\"464\" height=\"831\"><\/figure>\n<p>In the marked first section, we can see the host name that we have configured <code>dev.techiescamp.com<\/code>.<\/p>\n<p>In the Parent Refs section, we can see that the <strong>Gateway<\/strong> is configured with the <strong>HTTPRoute<\/strong>.<\/p>\n<p>The first part of the rules section indicates the first application (<code>Green<\/code>) and the condition, which means that if you want to access that application, you have to use <code>\/green<\/code> the path along with the host name <code>dev.techiescamp.com<\/code>.<\/p>\n<p>The second section is for the second application (<code>Orange<\/code>), here one condition we have changed is the path, we have to use <code>\/orange<\/code> with the hostname <code>dev.techiescamp.com<\/code>.<\/p>\n<p>First, we can try to access the first application (<code>Green<\/code>)<\/p>\n<p>Open any of the web browser and paste the URL <code>dev.techiescamp.com\/green<\/code><\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/blog.techiescamp.com\/content\/images\/2025\/01\/image-68.png\" class=\"kg-image\" alt=\"the demo application output from the web browser\" loading=\"lazy\" width=\"886\" height=\"476\"><\/figure>\n<p>The application I have created for this demo will give the output of the intended Pod name and the IP.<\/p>\n<p>Here, we can see that the traffic has reached the first application (<code>Green<\/code>), so the routing is configured correctly.<\/p>\n<p>We can check this with CLI as well using the following command.<\/p>\n<pre><code class=\"language-bash\">curl dev.techiescamp.com\/green<\/code><\/pre>\n<p>Now, we can try to access the second application.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/blog.techiescamp.com\/content\/images\/2025\/01\/image-69.png\" class=\"kg-image\" alt=\"the demo application output from the web browser\" loading=\"lazy\" width=\"888\" height=\"427\"><\/figure>\n<p>The second application is also accessible, ensuring the configured path is done correctly and the Gateway API routs the traffic to the intended services.<\/p>\n<h2 id=\"expose-gateway-api-controller-as-node-port-service\">Expose Gateway API Controller as Node Port Service<\/h2>\n<p>Without the help of any Load Balancer, we can configure to reach the external traffic to the Gateway API Controller Pod with the help of Node Port service.<\/p>\n<p>This is not a proper way to route the traffic, even though if someone wants to try the Gateway API on their local <a href=\"https:\/\/devopscube.com\/upgrade-kubernetes-cluster-kubeadm\/\" rel=\"noreferrer noopener\">Kubeadm cluster<\/a>, this method would be helpful.<\/p>\n<p>For this, we have to modify the Gateway API Controller installations.<\/p>\n<blockquote><p>Note: Assuming, you don&#8217;t have the GatewayAPI CRDs and Controller on the cluster.<\/p><\/blockquote>\n<p>Install the Gateway API Custom Resource Definitions.<\/p>\n<pre><code class=\"language-bash\">kubectl apply -f https:\/\/github.com\/kubernetes-sigs\/gateway-api\/releases\/download\/v1.3.0\/standard-install.yaml<\/code><\/pre>\n<p>Before we install the <strong>Gateway API Controller<\/strong> (<code>Nginx Fabric Gateway Controller<\/code>), we need to create a custom Helm values file to customize the installation.<\/p>\n<p>We have already created one with a name of <code>custom-values.yaml<\/code><\/p>\n<p>Our intention is to change the controller service type from <strong>Load Balancer<\/strong> to <strong>Node Port.<\/strong><\/p>\n<p>Node Ports are linked to listener ports, so we will assign NodePorts for ports <strong>80, 8080, and 443<\/strong><\/p>\n<p>For example, when we create a Gateway with listener port <strong>80<\/strong>, the Data Plane service will be created with type <strong>NodePort<\/strong> of <strong><code>32000<\/code><\/strong>.<\/p>\n<p>Following is the contents of the <code>custom-values.yaml<\/code> file.<\/p>\n<pre><code class=\"language-yaml\">nginx: \n  service: \n    type: NodePort\n    nodePorts:\n    - port: 32000\n      listenerPort: 80\n    - port: 32500\n      listenerPort: 8080\n    - port: 32443\n      listenerPort: 443<\/code><\/pre>\n<p>This file overrides the default Helm configuration.<\/p>\n<p>This settings tells the Gateway API to use NodePort instead of LoadBalancer. So, this allow us to access the applications using the Node&#8217;s public IP.<\/p>\n<pre><code class=\"language-bash\">$ helm install ngf . -n nginx-gateway --create-namespace -f custom-values.yaml<\/code><\/pre>\n<p>After deployment, each time we create a Gateway, a Node Port service will be created for the Data Plane.<\/p>\n<p>Since there is no Load Balancer, traffic directly reaches the Node first, and then to the Gateway API Data Plane.<\/p>\n<p>In the example deployment, note down the NodePort number (32000) to access the application.<\/p>\n<p>We also need to update the <strong>Local DNS configuration<\/strong>.<\/p>\n<p>Instead of using the public IP of the Load Balancer, we use the public IP of the Node.<\/p>\n<p>To get the public IP of the Nodes, use the following command.<\/p>\n<pre><code class=\"language-bash\">kubectl get no -o wide<\/code><\/pre>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/blog.techiescamp.com\/content\/images\/2025\/01\/image-78.png\" class=\"kg-image\" alt=\"listing the kubernets nodes to identify  the public ips of the no\" loading=\"lazy\" width=\"1205\" height=\"347\"><\/figure>\n<p>We are mapping these public IPs in <code>\/etc\/hosts<\/code><\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/blog.techiescamp.com\/content\/images\/2025\/01\/image-79.png\" class=\"kg-image\" alt=\"local dns configuration for the gateway api controller \" loading=\"lazy\" width=\"721\" height=\"489\"><\/figure>\n<p>Now, we can use the hostname (<code>dev.techiescamp.com<\/code>) with the Node Port number <code>32000<\/code> to access the application.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/blog.techiescamp.com\/content\/images\/2025\/01\/image-82.png\" class=\"kg-image\" alt=\"the demo application output from the web browser\" loading=\"lazy\" width=\"975\" height=\"470\"><\/figure>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/blog.techiescamp.com\/content\/images\/2025\/01\/image-81.png\" class=\"kg-image\" alt=\"the demo application output from the web browser\" loading=\"lazy\" width=\"974\" height=\"485\"><\/figure>\n<p>We can use this method to test the Gateway API from the local Kubernetes clusters.<\/p>\n<h2 id=\"difference-between-kubernetes-ingress-and-gateway-api\">Difference between Kubernetes Ingress and Gateway API<\/h2>\n<ol>\n<li>Ingress primarily supports the HTTP and HTTPS network protocols [Layer 7 protocols], Gateway API as well as supports TCP, UDP, TLS, and gRPC (Example: Databases, Messaging systems).<\/li>\n<li>In Ingress, only one object <code>ingress<\/code> where we have to give all our routing configurations, but in Gateway API, dedicated resources for each type of protocol, such as <code>httproute<\/code>, <code>tcproute<\/code>, <code>tlsroute<\/code> and <code>udproute<\/code>.<\/li>\n<li>Ingress is limited to path-based and host-based routing, but the Gateway API can perform the custom HTTP header-based routing, weighted routing, canary and blue-green network routings.<\/li>\n<li>The Gateway API also required a controller similar to the Ingress, but the list of <a href=\"https:\/\/gateway-api.sigs.k8s.io\/implementations\/?ref=devopscube.com\">Gateway API controller<\/a> is quite extensive.<\/li>\n<\/ol>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>This is just an introduction to the Kubernetes Gateway API, so we just covered the basic path and host-based routing, which is similar to Ingress. There are many advanced concepts available, and we will explore them on the upcomming blogs.<\/p>\n<p>To learn more about the Gateway API, please refer to the&nbsp;<a href=\"https:\/\/kubernetes.io\/docs\/concepts\/services-networking\/gateway\/?ref=devopscube.com\" rel=\"noreferrer noopener\">official documentation<\/a>. If youwant to know what functions the Nginx Fabric Gateway Controller offers, you can refer to this&nbsp;<a href=\"https:\/\/docs.nginx.com\/nginx-gateway-fabric\/?ref=devopscube.com\" rel=\"noreferrer noopener\">official documentation.<\/a><\/p>\n<p><!--kg-card-begin: html--><br \/>\n <iframe loading=\"lazy\" src=\"https:\/\/embeds.beehiiv.com\/2a495ef4-3de7-4600-8a0d-de5dc968b372\" data-test-id=\"beehiiv-embed\" width=\"100%\" height=\"320\" frameborder=\"0\" scrolling=\"no\" style=\"border-radius: 4px; border: 2px solid #e5e7eb; margin: 0; background-color: transparent;\"><\/iframe><br \/>\n<!--kg-card-end: html--><\/p>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/kubernetes-gateway-api\/\" target=\"_blank\" rel=\"noopener noreferrer\">Kubernetes Gateway API Tutorial for Beginner&#x27;s \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/kubernetes-gateway-api\/<\/p>\n","protected":false},"author":1,"featured_media":296,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-295","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\/295","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=295"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/295\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/296"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=295"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=295"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=295"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}