Overview
What is Gateway API?
Gateway API is an official Kubernetes project designed to address cluster ingress and internal routing needs. It focuses on L4 and L7 routing, offering APIs for managing ingress, load balancing, and even experimental service mesh support.
As a direct successor to the much loved Kubernetes Ingress API, Gateway API became generally available with its v1.0.0 release on October 31, 2023. Since then, the project has continued to grow with a constant flow of contributions and releases.
Gateway API focuses on defining the standard that other providers can then implement. We’re starting to see an expanding list of implementations from some of the most popular networking projects like Envoy Gateway, Gloo Gateway, Istio, and NGINX Gateway Fabric. A comprehensive list of implementations can be found here including their current status.
Disambiguating Gateway API and API Gateway
It’s important to clarify the difference between Gateway API and an API Gateway, as understandably these terms can be confused.
- Gateway API: A Kubernetes native API that standardizes the way we configure and manage ingress traffic routing within a cluster. It focuses on controlling traffic flow at the infrastructure level by introducing Custom Resource Definitions (CRDs) that can configure your load balancer.
- API Gateway: The concept of anything that is responsible for managing API requests between clients and backend services. API Gateways typically offer features like authentication, rate limiting, request transformation, etc. They’re used to manage and secure API endpoints for clients.
Why not just use Ingress API?
You may be wondering why we would even need Gateway API if Ingress API already exists and seems to work well enough.
While Ingress API is straightforward and effective for basic use cases, as your organization scales, maintenance of the Ingress resources will grow more difficult. You could find yourself in a situation where application developers are stepping on the toes of cluster operators and vice versa because they’re all making changes to the same underlying resources.
Gateway API also addresses some of the limitations of Ingress API without sacrifice in the simplicity department. Let’s dive into some specific limitations:
- Expressiveness: Ingress API supports simple HTTP/HTTPS routing but lacks features like header-based or query-parameter-based routing.
- Separation of concerns: Ingress API combines routing rules, load balancing, and backend configurations in a single resource. This tight coupling can cause management headaches, especially in large or multi-tenant setups. For example, an application developer who wants to add a new route for their service needs to interact with the same API that handles backend configurations and infrastructure-level settings. This increases the likelihood of misconfigurations and complicates workflows.
- Protocol support: Ingress API is primarily focused on HTTP/HTTPS traffic and offers limited support for other protocols like TCP, UDP, or gRPC.
- Extensibility: While Ingress API is extendable via annotations, the approach can be inconsistent and often vendor specific which leads to portability issues.
- Sub-par observability: The single API approach limits feedback and status reporting. Debugging misconfigurations or runtime behavior is challenging due to the lack of granular insights.
Gateway API aims to address all of these concerns by introducing several CRDs that align with specific user personas (more on this later). This simplifies the separation of user responsibilities while enhancing flexibility and control.
Perhaps the most important reason to start using Gateway API instead of Ingress is that the Ingress API is officially frozen. This means no new features or fixes will be added to Ingress. The Ingress API is legacy at this point and continuing to use it not only results in taking on tech debt, but also means you’ll miss out on all the modern features and improvements that are being introduced through Gateway API.
Due to how Gateway API was designed with a focus on particular personas managing particular resources, there isn’t a direct one to one translation of Ingress resources to Gateway API resources. But not to worry, the Gateway API team has provided some great docs on how to migrate from Ingress to Gateway API. There are also tools available like the aptly named ingress2gateway which can help with migrating from Ingress to Gateway API. Keep in mind that you should always test and verify the results of the automated migration.
How Gateway API works
Introducing the new CRDs
The Gateway API specification introduces multiple new CRDs to enhance flexibility and scalability in managing ingress and inter-cluster routing. These include:
- GatewayClass: Defines a class of Gateways that share common configurations, like the underlying infrastructure or controller (e.g. Kuma, Istio, or Gloo Gateway). This allows infrastructure providers to standardize Gateway behaviour across clusters.
- Gateway: Represents an instance of a GatewayClass and defines the entry points for traffic into the cluster. It essentially defines a request for a specific load balancer config. Until the Gateway CR is created, no actual infrastructure will be provisioned.
- Route: These CRDs specify routing behaviours for requests from the Gateway listener to a Service. There are multiple CRDs defined which add support for different protocols. The different supported RouteTypes include:
- GRPCRoute
- HTTPRoute
- TCPRoute (experimental)
- TLSRoute (experimental)
- UDPRoute (experimental)
Each CRD is designed with a specific use case in mind. For a complete specification, refer to the official Gateway API documentation here.
Note: Gateway API has a strict graduation criteria for a feature to move from the experimental to the standard release channel. While to some it may feel frustrating to have to wait for a particular feature to reach GA, this process helps ensure that the standard channel remains stable and maintains backwards compatibility.
Gateway to Route relationships
As mentioned above, each Gateway needs to reference a specific GatewayClass, but there are multiple ways you can link a Route to a Gateway. Here are some common configuration patterns that you can use:
- Single Route to a single Gateway: A single Route resource is attached to a single Gateway. This is ideal for tightly scoped use cases where the Gateway serves a specific application or service.
- Multiple Routes on a single Gateway: A single Gateway can serve multiple Route resources across different namespaces. This is a common pattern for multi-tenant environments, where teams or applications share a Gateway but have independently managed Routes.
- Single Route on multiple Gateways: A single Route can be linked to multiple Gateways. This is particularly useful for scenarios where an application needs to be exposed across different environments (e.g., public and private networks) or through different IPs or load balancers.
Service mesh integration and experimental features
In addition to its primary use case for ingress, Gateway API also supports linking Route resources directly to services, bypassing the need for Gateway and GatewayClass resources. By doing this, you’d effectively be utilizing your provider's service mesh. This is an experimental feature being actively developed as a part of the GAMMA Initiative (Gateway API for Mesh Management and Administration).
Gateway API’s target user personas
Each of the CRDs discussed in the above section is intended to be managed by a user of a particular persona. The personas that Gateway API are designed for are:
- Infrastructure provider: Focused on creating and managing GatewayClasses. Create controllers which decouple the mechanism for implementing Gateways from the end user.
- Cluster operator: Responsible for configuring Gateway resources. They define cluster entry points, manage listeners, and establish gateway-level policies.
- Application developer: Manages the Route CRDs (HTTPRoute, GRPCRoute, etc). Developers configure routing rules for their specific applications, allowing for granular control without impacting the underlying infrastructure.
Naturally, depending on the structure of your organisation, users may wear multiple persona hats. But this separation of concerns via personas allows even very mature organizations to assign users to distinct roles where they can easily operate together with little to no friction.
RBAC
Gateway API’s design naturally aligns with Kubernetes Role-Based Access Control (RBAC). A simple 3 tier model of write permissions by role can look something like this (where a Role is created for each persona):
Given that you’ve configured RBAC as defined above, you’ll know that you’ve secured your infrastructure configuration from unwanted changes by unauthorized actors. This alignment enforces secure and efficient workflows, allowing teams to focus on their responsibilities without overstepping any boundaries.
Gateway API in action
Now that we’ve spent a lot of time going over the theory, it’s time to finally see how it all works in practice.
We’ll create a local Kind cluster, set up Kuma as the Gateway provider, and explore various routing features like path-based routing, header modifications, path rewrites, and traffic splitting.
We chose Kuma because its Gateway controller implementation has reached GA status, but we very well could have gone through the demo with one of the other Gateway providers. You can follow along with the complete demo steps in this GitHub repository. All the Kubernetes manifests used should be viewed from there.
Prerequisites
Ensure you have the following tools installed:
- Docker
- Kind
- cloud-provider-kind
- Helm
Cluster setup
Start by creating a Kind cluster and installing the Gateway API CRDs, Kuma, and the necessary namespaces
kind create cluster
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
helm repo add kuma https://kumahq.github.io/charts
helm repo update
helm install --create-namespace --namespace kuma-system kuma kuma/kuma
kubectl apply -f manifests/1-kuma.yaml
The helm install of Kuma will already handle setting up our GatewayClass.
Gateway creation
Now we want to create two Gateways which reference the Kuma GatewayClass
- A shared Gateway (shared-gw) that allows Routes from all namespaces to attach to it
- A dedicated Gateway (dedicated-team-c-gw) that only accepts Routes from within the same namespace (team-c-infra)
kubectl apply -f manifests/2-gateways.yaml
In another terminal we can run `sudo cloud-provider-kind` to help us simulate an external IP on our LoadBalancer type services (i.e. the services attached to our Gateways). Once this is done, just run the following command to get the external IP of our shared Gateway.
export PROXY_IP=$(kubectl get svc --namespace shared-infra shared-gw -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
Our cluster should now look like this:
Let’s test the connection:
curl ${PROXY_IP}:80
This is a Kuma MeshGateway. No routes match this MeshGateway!
Nice, it works! It looks like the Cluster Operator’s job is done now. Lets move onto getting our Application Developers to set up their Routes!
HTTPRoute Creation
We now want to set up two Routes that each send traffic to two distinct services. These Routes are going to demonstrate:
- Path-based routing to direct requests to the appropriate service
- Header modification to inject a new header on each request
- Path rewriting to replace the matched request path to `/api`
- Traffic splitting between two versions of a backend service (80% to v1 and 20% to v2)
Apply the routes with:
kubectl apply -f manifests/3-routes.yaml
Once these Routes are deployed, you should be able to confirm they were accepted by inspecting their status:
Status:
Parents:
Conditions:
Last Transition Time: 2025-01-11T04:40:07Z
Message:
Observed Generation: 1
Reason: ResolvedRefs
Status: True
Type: ResolvedRefs
Last Transition Time: 2025-01-11T04:39:21Z
Message:
Observed Generation: 1
Reason: Accepted
Status: True
Type: Accepted
To prove that not all Routes will be accepted, we can also try applying a Route from namespace `team-b` to the `dedicated-team-c-gw`. This was the Gateway that only accepts routes from within its namespace.
Try applying the bad Route with:
kubectl apply -f manifests/3-routes-failed.yaml
You’ll notice that the Route is created, but when inspecting its status, you’ll see the below indicating that it hasn’t been accepted.
Status:
Parents:
Conditions:
Last Transition Time: 2025-01-11T04:39:29Z
Message: backend reference references a non-existent Service "team-b/team-b-failed-app"
Observed Generation: 1
Reason: BackendNotFound
Status: False
Type: ResolvedRefs
Last Transition Time: 2025-01-11T04:39:29Z
Message:
Observed Generation: 1
Reason: NotAllowedByListeners
Status: False
Type: Accepted
Our setup is starting to all come together. This is where we’re at now:
Now it's time to deploy our backend apps that traffic will be routed to!
Deploying the backend apps
Our backend app is going to be a really simple Go server which does the following:
- Handle requests from the /api endpoint (the path that was rewritten)
- Log the value of the `X-Request-From` header which was added by the HTTPRoute
- Log the value of the server that handled the request to give us a rough idea of how the traffic splitting is going
- Return a 200 OK
The entirety of the Go server can be seen here:
package main
import (
"fmt"
"log"
"net/http"
"os"
)
func main() {
http.HandleFunc("/api", apiHandler)
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Printf("Server starting on port %s", port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}
func apiHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("Received request on %s", r.URL.Path)
// Print the X-Request-From header
requestFrom := r.Header.Get("X-Request-From")
if requestFrom == "" {
requestFrom = "Unknown"
}
log.Printf("X-Request-From: %s", requestFrom)
// Respond to the request
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Hello from %s\n", os.Getenv("APP_NAME"))
fmt.Fprintf(w, "Received from: %s\n", requestFrom)
}
We can build and load an image of our application onto our cluster, and then deploy the Services and Deployments to our cluster with the following commands:
docker build -t gateway-api-demo-server:latest .
kind load docker-image gateway-api-demo-server:latest
kubectl apply -f manifests/4-apps.yaml
And with that, our cluster has reached its final state:
Now with `cloud-provider-kind` running, we can test the setup by executing either of the following requests:
- curl ${PROXY_IP}:80/team-a
- curl ${PROXY_IP}:80/team-b
Executing a few of those commands in succession will yield results like this:
curl ${PROXY_IP}:80/team-a
Hello from team-a-app-v1
Received from: Gateway-API-Demo
curl ${PROXY_IP}:80/team-a
Hello from team-a-app-v1
Received from: Gateway-API-Demo
curl ${PROXY_IP}:80/team-a
Hello from team-a-app-v2
Received from: Gateway-API-Demo
curl ${PROXY_IP}:80/team-a
Hello from team-a-app-v1
Received from: Gateway-API-Demo
curl ${PROXY_IP}:80/team-b
Hello from team-b-app-v2
Received from: Gateway-API-Demo
curl ${PROXY_IP}:80/team-b
Hello from team-b-app-v1
Received from: Gateway-API-Demo
curl ${PROXY_IP}:80/team-b
Hello from team-b-app-v1
Received from: Gateway-API-Demo
If you run these commands enough, it’ll be abundantly clear that the majority of the requests are making their way to the v1 versions of the apps. It’s also good to see that the header added by the HTTPRoute is present and that our server was able to handle requests to /api because the request paths were rewritten.
Final thoughts and next steps
Gateway API is an important evolution in managing ingress and inter-cluster networking in Kubernetes. Its modular design and persona-driven approach provide robust tools to handle diverse routing scenarios while maintaining simplicity and flexibility. Routes can be configured in so many ways that can enable strategies like canary traffic rollout, mirroring traffic to test environments, and so on.
With its emphasis on scalability, extensibility, and separation of concerns, Gateway API empowers organizations to build resilient and efficient networking setups tailored to their needs. Whether you’re just getting started or managing a complex environment of multiple Kubernetes clusters, you shouldn’t ignore Gateway API.
Consider using Gateway API for your next project. It’s not only an improvement over Ingress API but also a forward-looking investment in Kubernetes-native networking. You can try out some of the most popular Gateway API downstream implementations via Palette. We support Cilium, Gloo, Istio, Kong, and NGINX to name a few. Check out our Packs List and get in touch for a demo.