# Kubernetes Networking : Part 5 : Gateway API Explained

In the last blog, we discussed how Ingress plays a vital role in routing external traffic into your Kubernetes cluster. If you're just joining this series or are new to Kubernetes, I'd highly recommend checking out the previous four [networking blogs](https://claybrainer.com/series/kubernetes-in-detail) before continuing with this one.

That being said, if you're already familiar with Kubernetes and Kubernetes networking, grab a cup of coffee ☕ and join me as we explore the Gateway API.

# Why was Gateway API introduced ?

Before we understand what Gateway API is, let's first answer an important question.

**Why did Kubernetes introduce another API when Ingress was already working?**

If you've read my [previous blog](https://claybrainer.com/kubernetes-networking-part-4-ingress-components-architecture-and-routing-strategies), you know that Ingress solved one of the biggest problems with exposing applications.

Without Ingress, every application needed its own `LoadBalancer` Service. In a production environment running dozens or even hundreds of applications, this quickly becomes expensive and difficult to manage.

Ingress solved this by allowing multiple applications to share a single Load Balancer. Using host-based and path-based routing, one Load Balancer could expose an entire fleet of applications.

Sounds perfect, right? Well... not exactly.

## The Problem with Ingress

Ingress mainly consists of two components:

*   **Ingress Resource** – A Kubernetes object where you define routing rules.
    
*   **Ingress Controller** – The component that watches those rules and configures the underlying proxy (NGINX, Cilium, Traefik, HAProxy, etc.).
    

Technically, Ingress works very well. The challenge isn't the technology it's **who manages it**.

Let's understand this with a simple example. Imagine your company has an e-commerce application with multiple teams.

*   Team A owns the **User Service**
    
*   Team B owns the **Product Service**
    
*   Team C owns the **Payment Service**
    

All of these services are exposed through a single Ingress.

`shop.example.com/user -> user-service`

`shop.example.com/product -> product-service`

`shop.example.com/payment -> payment-service`

Example ingress manifest:

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ecommerce-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: shop.example.com
    http:
      paths:
      - path: /user
        pathType: Prefix
        backend:
          service:
            name: user-service
            port:
              number: 80

      - path: /product
        pathType: Prefix
        backend:
          service:
            name: product-service
            port:
              number: 80

      - path: /payment
        pathType: Prefix
        backend:
          service:
            name: payment-service
            port:
              number: 80
```

Notice something interesting? Every application's routing configuration is stored in a **single Ingress resource**.

Now imagine the Payment team wants to expose a new endpoint. To do that, they need to modify the same Ingress resource that everyone else uses. Likewise, if the Product team wants to modify its routing, it still needs access to this shared Ingress resource.

This creates a few problems:

*   Every team needs permission to edit the same Ingress resource.
    
*   A small mistake can accidentally impact another team's routes.
    
*   Most organizations don't allow application teams to modify shared networking resources.
    
*   As a result, every routing change has to go through the DevOps or Platform team.
    

Now imagine dozens of teams raising tickets every day just to add or update a route.

Instead of developers deploying independently, they now depend on the Platform or DevOps team for even small networking changes. This slows down releases and creates an unnecessary operational bottleneck. **This is one of the biggest reasons Gateway API was introduced.**

**It separates infrastructure ownership from application routing, allowing each team to manage only what they own while the Platform team continues to control the shared networking infrastructure.**

> Ingress was designed to expose applications. Gateway API was designed to expose applications at scale across multiple teams.

## **What Gateway API is ?**

Now that we understand the problem, let's see how Gateway API solves it.

Instead of having one Ingress resource that everyone edits, Gateway API splits the responsibilities into different resources.

Think of it this way:

*   The **Platform (DevOps) Team** manages the networking infrastructure.
    
*   The **Application Teams** manage only their application's routing.
    

This separation is exactly what was missing in Ingress.

Let's revisit our ecommerce application. Previously, we had a single Ingress like this:

`shop.example.com/user -> user-service`

`shop.example.com/product -> product-service`

`shop.example.com/payment -> payment-service`

Every team had to edit the same Ingress resource whenever they wanted to expose a new endpoint.

With Gateway API, things work differently. The Platform team first creates a **Gateway**.

### Gateway ?

Think of the Gateway as the **main entry point** into your Kubernetes cluster. It defines things like:

*   Which ports should be exposed (HTTP/HTTPS)
    
*   Which hostname or domain should be accepted
    
*   Which teams are allowed to attach routes
    
*   TLS certificates
    
*   Listener configuration
    

*Notice something? There are* ***no application routes here***.

The Platform team is responsible only for setting up the networking infrastructure. Now comes the interesting part.

Each application team creates its own **HTTPRoute**. For example,

*   the User team creates: `/user - user-service`
    
*   The Product team creates: `/product - product-service`
    
*   etc
    

Each team owns only its own HTTPRoute resource. If the Payment team wants to add a new endpoint tomorrow, they only modify their HTTPRoute. They don't touch the User team's routes. They don't touch the Product team's routes. They don't even need permission to edit the Gateway.

The Platform team continues managing the shared networking infrastructure, while every application team manages only its own routing rules.

This removes the operational bottleneck we discussed earlier. Developers can independently deploy routing changes without waiting for the DevOps team, and the Platform team doesn't have to review every small routing update.

This separation of responsibilities is one of the biggest design improvements introduced by Gateway API.

Now that we've learned how Gateway API solves the problems of Ingress, let's dive a little deeper and understand its core components.

# Components of Gateway API

Gateway API defines multiple resources, but for exposing HTTP applications, there are **three important resources** that you'll work with most of the time.

*   Gateway Class
    
*   Gateway
    
*   HTTPRoute
    

One of the main goals of Gateway API is **separation of responsibilities**. Instead of having one resource that everyone edits (like Ingress), each resource has a specific purpose and is usually managed by a different team.

To understand these resources, let's use a simple real world analogy. Imagine your company is constructing a new office building.

*   First, the architects decide **which construction company** will build the office.
    
*   Once that's decided and office is built, the infrastructure team designs and manages the building's **main entrance**, deciding **who can enter** and **through which doors**.
    
*   Finally, each department places its own **signboards**, directing visitors to the correct department enterance.
    

Gateway API follows the same idea.

*   **GatewayClass** decides **which gateway implementation** will be used.
    
*   **Gateway** creates the **main entry point** into your Kubernetes cluster.
    
*   **HTTPRoute** defines **where incoming requests should be routed.**
    

Let's understand each one in detail.

## Gateway Class

GatewayClass is the **top-most resource** in Gateway API.

Its job is to tell Kubernetes **which Gateway implementation (provider)** should manage your Gateways. Think of it like hiring a construction company before building your office.

You first decide **who is going to build it**. Will it be Company A? or Company B. Once you've made that decision, every building constructed under that contract follows that company's standards.

GatewayClass works in exactly the same way. You choose which Gateway provider will implement your Gateway resources. Some popular Gateway providers include:

*   **Cilium Gateway**
    
*   **NGINX Gateway Fabric**
    
*   **Envoy Gateway**
    
*   **Kong Gateway**
    
*   **HAProxy Kubernetes Ingress Controller**
    
*   **Traefik**
    

Each provider has its own Gateway controller that watches Gateway API resources and configures the underlying data plane accordingly.

For example, if you create a GatewayClass for **Cilium**, the Cilium Gateway controller manages all Gateways that reference that class. Likewise, if you create a GatewayClass for **NGINX Gateway Fabric**, the NGINX Gateway controller becomes responsible for those Gateways.

Since GatewayClass defines the infrastructure implementation for the entire cluster, it is a **cluster scoped resource** and is typically managed by the **Platform or Infrastructure team**.

Although Kubernetes allows you to create multiple GatewayClasses in the same cluster for example, one using Cilium and another using NGINX Gateway Fabric in most production environments, organizations standardize on a single provider unless there is a specific business requirement.

Example manifest of gateway class

```yaml
apiVersion: gateway.networking.k8s.io/v1 
kind: GatewayClass 
metadata: 
  name: cilium 
spec: 
  controllerName: io.cilium/gateway-controller
```

Let's understand what each field means:

*   **apiVersion** – Specifies the version of the Gateway API.
    
*   **kind** – Tells Kubernetes that we're creating a `GatewayClass` resource.
    
*   [**metadata.name**](http://metadata.name) – The name of the GatewayClass. We'll reference this later while creating a `Gateway`.
    
*   **spec.controllerName** – Identifies the Gateway controller that will manage this GatewayClass. Since we're using Cilium, the controller name is `io.cilium/gateway-controller`.
    

## Gateway

Now that we've selected our Gateway provider using a **GatewayClass**, the next step is to create a **Gateway**.

Continuing with our office building analogy...

We've already decided which construction company is going to build and manage our building. Now, it's time to design the **main entrance**. Think of the Gateway as the **main entrance** to your office building.

The **infrastructure team** decides:

*   Which entrances should be open (HTTP, HTTPS, or both)
    
*   Which domain names should visitors use
    
*   Which TLS certificate should secure the entrance
    
*   Which application teams are allowed to attach their routes
    

Notice that the Gateway **doesn't know anything about your applications**. It doesn't know where the User Service is. It simply defines **how traffic is allowed to enter the Kubernetes cluster**.

Once the Gateway is created, multiple application teams can attach their own routing rules without modifying the Gateway itself. *This is one of the biggest improvements over Ingress.*

With Ingress, networking configuration and application routing were often defined in the same resource. With Gateway API, those responsibilities are separated.

The Platform or Infrastructure team owns the **Gateway**, while application teams own their **HTTPRoute** resources. This allows each team to work independently without interfering with one another.

Let's look at what a Gateway resource looks like.

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: web-gateway
spec:
  gatewayClassName: cilium
  listeners:
  - name: http
    protocol: HTTP
    port: 80
```

Let's understand the important fields in the Gateway manifest.

*   **apiVersion** – Specifies the version of the Gateway API that Kubernetes should use.
    
*   **kind** – Indicates that we're creating a `Gateway` resource.
    
*   [**metadata.name**](http://metadata.name) – The name of the Gateway. Other resources, such as `HTTPRoute`, will reference this Gateway to attach their routing rules.
    
*   **gatewayClassName** – Specifies which `GatewayClass` should manage this Gateway. In our example, we're using the `cilium` GatewayClass that we created earlier. This tells Kubernetes to let the Cilium Gateway controller manage this Gateway.
    
*   **listeners** – Defines how the Gateway accepts incoming traffic. A Gateway can have one or more listeners.
    
    In our example, the listener is configured with:
    
    *   **name** – A unique name for the listener.
        
    *   **protocol** – The protocol that the Gateway accepts, such as `HTTP`, `HTTPS`, `TLS`, or `TCP`.
        
    *   **port** – The port on which the Gateway listens for incoming traffic.
        

## HTTPRoute

So far, we've selected our Gateway provider using a **GatewayClass** and created a **Gateway** to act as the main entry point into our Kubernetes cluster.

But there's still one important question. **Once a request enters the cluster, how does it know which application to reach?**

This is where **HTTPRoute** comes into the picture.

Let's continue with our office building analogy.

A visitor enters the building through the main entrance. Now they need directions. They need to go to the Finance department which room they want to enter ?

This is exactly what the signboards inside a building do—they guide visitors to the correct destination.

An **HTTPRoute** works in the same way. It defines how incoming HTTP requests should be matched and which backend service they should be forwarded to.

Unlike Ingress, each application team can create and manage its own HTTPRoute without modifying the shared Gateway. For our ecommerce application, each team owns its own HTTPRoute.

The User team creates an HTTPRoute for `/user`.

The Product team creates an HTTPRoute for `/product`.

The Payment team creates an HTTPRoute for `/payment`.

Each team is responsible only for its own routing configuration. This separation allows multiple teams to work independently while sharing the same Gateway.

Let's look at an example HTTPRoute.

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: user-route
spec:
  parentRefs:
  - name: web-gateway

  hostnames:
  - shop.example.com

  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /user

    backendRefs:
    - name: user-service
      port: 80
```

Let's understand the important fields:

*   [**metadata.name**](http://metadata.name) – The name of the HTTPRoute resource.
    
*   **parentRefs** – Specifies which Gateway this route should attach to. In our example, this route is attached to the `web-gateway`.
    
*   **hostnames** – Defines the hostname that this route should match.
    
*   **rules** – Contains one or more routing rules.
    
*   **matches** – Specifies the conditions that incoming requests must satisfy. Here, we're matching requests whose path starts with `/user`.
    
*   **backendRefs** – Specifies the backend Service that should receive the traffic once the request matches the rule.
    

## Overall Flow

*   A request to `shop.example.com/user/profile`
    
*   Arrives at the **Gateway**
    
*   The Gateway forwards it to the attached **HTTPRoute**
    
*   The HTTPRoute matches the `/user` path
    
*   The request is finally sent to the `user-service`
    

# Hands On Lab

We've covered the concepts behind Gateway API and understood how its components work together. But the best way to learn is by seeing it in action.

In this hands on lab, we'll deploy a Gateway, create an HTTPRoute, and expose our application using Gateway API.

Feel free to follow along using the **README** in the [GitHub repository](https://github.com/naveenkumarvr/kubernetes_learning/tree/main/networking/03-gateway-api). By the end of this lab, you'll have a working Gateway API setup and a much clearer understanding of everything we've learned so far.

Let's get our hands dirty!

# Conclusion

Congratulations! 🎉 If you've made it this far.

In this blog, we learned why the Kubernetes community introduced Gateway API and how it addresses some of the limitations of Ingress, especially in environments where multiple teams share the same cluster.

We explored the three core resources you'll use most often:

*   **GatewayClass** – Specifies which Gateway provider manages your Gateways.
    
*   **Gateway** – Defines how traffic enters your Kubernetes cluster.
    
*   **HTTPRoute** – Defines where that traffic should be routed.
    

Most importantly, we learned that Gateway API isn't just another way to expose applications. It introduces a clear separation of responsibilities, allowing Platform teams to manage the networking infrastructure while application teams independently manage their own routing rules.

This makes Gateway API more scalable, easier to manage, and a better fit for modern production environments.

I hope this blog helped you understand not just **how** Gateway API works, but also **why** it was introduced in the first place.

In the next blog, we'll dive deeper into the networking concepts of kubernetes.

Until then, keep learning, keep experimenting, and as always...

**Happy Kubernetes! 🚀**
