A Practical Guide to Kubernetes Gateway API
In the early days of Kubernetes, Ingress was the standard mechanism for routing external traffic into a cluster. However, as microservice architectures matured, engineering teams scaled, and traffic governance requirements grew more complex, the inherent limitations of Ingress became increasingly obvious.
To address these structural problems at their root, the Kubernetes Special Interest Group for Networking (SIG-Network) introduced the Gateway API. Far from being an incremental patch on Ingress, the Gateway API fundamentally decouples traffic configuration across infrastructure, platform, and application domains at the schema level.
Designed for engineers and SREs looking to understand the Gateway API, this article starts from its core architecture, dissects the notorious “Annotation hell” of traditional Ingress, and walks through a practical refactoring example, a migration cheat sheet, and a controller selection guide.
What Is the Gateway API: From Subdivided Apartments to Modern Commercial Buildings
The fastest way to grasp the Gateway API is to examine how it redraws the boundaries of networking resource management.
Imagine a Kubernetes cluster as a physical building:
Traditional Ingress: An Aging Subdivided Apartment
Who manages the front door, who holds the keys, and what notices hang at the entrance are all crammed into a single lease agreement (a single Ingress resource). If a tenant wants to replace a lock (configure CORS) or update their doorplate (URL rewrite), they have to scribble in the margins of that contract (stuffing annotations in). A single errant scribble can easily break the entire building’s access control.
Gateway API: A Modern Commercial Office Building
- The Building Developer (Infrastructure Provider): Defines the specifications for access turnstiles and elevator systems (
GatewayClass). - Building Management & Facilities (Cluster Operator / SRE): Installs the main lobby entrance, staffs security patrols, and provisions the central street address and SSL certificates (
Gateway). - Tenant Companies on Each Floor (Application Developer): Independently define their internal reception flows and conference room routing (
HTTPRoute). They simply attach their routes to the ground-floor gateway without ever touching or compromising the building’s perimeter security.
This model of decoupled roles and separated responsibilities is the core design philosophy of the Gateway API.
The Three Core Roles and CRD Architecture
The Gateway API leverages Kubernetes Custom Resource Definitions (CRDs) to break down traffic governance into a clean, three-tiered structure:
[1] Infrastructure Provider
+-----------------------------------------------+
| GatewayClass: envoy-gateway, cilium |
+-----------------------------------------------+
| Defines underlying
| implementation spec
v
[2] Cluster Operator / SRE
+-----------------------------------------------+
| Gateway: Port 80/443, TLS cert, external IP |
+-----------------------------------------------+
| |
| Allows HTTPRoute |
| attachment |
v v
[3] Application Developer
+----------------------+ +----------------------+
| HTTPRoute: orders | | HTTPRoute: users |
| /orders -> order-svc | | /users -> user-svc |
+----------------------+ +----------------------+
| |
v v
+----------------------+ +----------------------+
| Service: order-svc | | Service: user-svc |
+----------------------+ +----------------------+
These three core resources correspond to explicit role boundaries:
GatewayClass: Managed by the Infrastructure Provider or cloud vendor. It declares the underlying technology powering the gateway (such as Envoy, Cilium, or a cloud provider’s ALB), conceptually analogous toStorageClassin Kubernetes storage.Gateway: Maintained by the Cluster Operator or platform engineering team. It represents the physical or virtual entry point receiving external traffic (typically mapping to a load balancer instance or IP address). It defines the listening ports (80 and 443), protocols (HTTP, HTTPS, TCP), and TLS certificate attachments.HTTPRoute: Authored independently by Application Developers (microservice teams). It defines concrete traffic routing logic, such as “route requests with path/api/v1/orderstoorder-svc.” Developers do not need to know the underlying load balancer’s physical IP; they simply declare whichGatewayto attach to viaspec.parentRefs.
Beyond the core HTTPRoute used in production, the specification also defines dedicated route types for other protocols—such as the generally available (GA) GRPCRoute, alongside Layer 4 transport routes like TCPRoute, UDPRoute, and TLSRoute—for teams to adopt as needed.
Why Ingress Falls Short: Three Structural Pain Points
Ingress was originally introduced in 2015 with Kubernetes 1.1. During the nascent stages of microservices, it offered a significantly more flexible and cost-effective Layer 7 (HTTP) routing mechanism than NodePort and LoadBalancer.
Yet in the decade since, the governance complexity of modern production environments has far outpaced what the original architecture anticipated, exposing three glaring structural pain points.
Pain Point 1: The Notorious “Annotation Hell”
In the official Ingress specification, resource attributes are Spartan: essentially just rules (domains and paths) and backend (target Services). Yet real-world production workloads inevitably demand advanced traffic capabilities:
- URL rewriting and redirects
- Cross-Origin Resource Sharing (CORS) header manipulation
- Request timeouts and retry policies
- Canary traffic splitting (traffic weighting)
- Bandwidth throttling and rate limiting
Because the Ingress schema lacked native support for these capabilities, Ingress controller implementations (such as NGINX, Traefik, HAProxy, and Emissary) were forced to invent proprietary metadata.annotations to bridge the gap. In day-to-day operations, this introduced three critical problems:
- No type safety; typos fail silently:
Annotations are unstructured string key-value pairs. If you accidentally mistype
proxy-connect-timeoutasproxy-connet-timeout, the Kubernetes API Server accepts the resource without error. The configuration silently fails in production, going unnoticed until backends start timing out under load. - Vendor lock-in and prohibitive migration costs: NGINX relies on regex capture groups in annotations for URL rewrites; Traefik requires proprietary Middleware CRDs; HAProxy uses yet another convention for header manipulation. Once a team decides to migrate from Ingress-NGINX to another solution, hundreds of Ingress manifests across the cluster must be completely rewritten.
- Layered black magic and spiraling maintenance overhead: A single Ingress manifest frequently accumulates dozens of lines of untyped annotations lacking any semantic constraints. They cannot be validated via standard unit tests or schema checkers, turning troubleshooting into a guessing game.
Core Comparison: String Annotations vs. Strongly Typed Specifications
Compare URL rewriting and canary splitting—the two declaration philosophies are night and day:
# Traditional Ingress: Relies on non-standard string annotations, lacks type checking, and syntax varies by controller
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /api/$1
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "20"
# Gateway API: First-class filters and native weights, strictly validated by the API Server schema
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /api
backendRefs:
- name: order-service-v1
port: 8080
weight: 80
- name: order-service-v2
port: 8080
weight: 20
In the Gateway API, regardless of which controller runs under the hood, filters.urlRewrite and backendRefs.weight carry vendor-neutral standardized semantics, strictly validated by the API Server at admission time.
Pain Point 2: Monolithic Resources Conflating Cross-Team Responsibilities
Under the Ingress architecture, a single YAML manifest must accommodate both infrastructure settings (such as TLS certificate Secret references and global hostnames) and application routing logic (such as microservice path mappings).
This design directly fuels organizational friction:
- To tweak a routing path, application developers need edit access to the Ingress resource, creating the risk of accidentally altering cluster-wide TLS certificates or global timeout values.
- To safeguard stability, platform teams often lock down Ingress permissions—forcing application teams to submit tickets to SREs for routine route updates, slowing delivery cadence.
The Gateway API cleanly resolves this through the strict separation of Gateway (owned by SRE) and HTTPRoute (owned by application teams), paired with native Kubernetes RBAC to re-establish clean operational boundaries.
Pain Point 3: Severely Constrained Cross-Namespace Routing
In microservice architectures, different product teams typically operate within isolated namespaces.
By default, an Ingress can only route traffic to Services within its own namespace. Directing traffic from a single entry domain to Services across multiple namespaces often requires proprietary controller annotations or convoluted ExternalName service workarounds.
The Gateway API introduces native support for cross-namespace route binding with fine-grained authorization: a central Gateway controls which namespaces are permitted to attach routes via allowedRoutes.
When cross-namespace references extend to sensitive resources—such as a Gateway referencing a TLS Secret located in another namespace, or a Route forwarding to a backend Service in a different namespace—the target resource owner must explicitly grant permission using a ReferenceGrant, combining architectural flexibility with defense-in-depth security.
Hands-On Refactoring: Migrating from Ingress-NGINX to Gateway API
To ground these architectural concepts in practical code, let’s walk through an e-commerce platform running on shop.example.com and contrast how identical routing requirements are configured across both architectures.
Scenario Setup
Our objective is to expose an external entry point for an online shop with four common requirements:
- Cluster-wide HTTPS: Terminate TLS using the
shop-tls-secretcertificate. - Static Frontend: Forward requests hitting the root path
/to the static frontend servicefrontend-svc:80. - User Service (URL Rewrite): External requests to
/api/v1/usersmust have their prefix stripped and rewritten to/usersbefore reachinguser-svc:8080. - Order Service (Canary Split): For requests hitting
/api/v1/orders, roll out a new versionorder-svc-v2:8080, splitting traffic with 80% routed toorder-svc-v1and 20% routed to the canaryorder-svc-v2.
Before: The Compromises of Ingress-NGINX
In traditional Ingress-NGINX, because the core spec cannot express weighted splits within a single rule, engineers are forced to maintain two separate Ingress manifests:
File 1: Primary Ingress (shop-ingress-main.yaml snippet)
Handles static frontend routing, user service URL rewriting, and the baseline order service:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ingress-main
annotations:
# URL rewriting requires enabling regexes and writing obscure capture groups
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
tls:
- hosts:
- shop.example.com
secretName: shop-tls-secret
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-svc
port: { number: 80 }
- path: /api/v1(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: user-svc
port: { number: 8080 }
- path: /api/v1/orders
pathType: Prefix
backend:
service:
name: order-svc-v1
port: { number: 8080 }
File 2: Canary Shadow Ingress (shop-ingress-canary.yaml snippet)
To route 20% of traffic to the new version, you must duplicate an almost identical Ingress manifest annotated with canary tags.
When the Ingress-NGINX controller detects two Ingresses pointing to the same host, with one bearing canary: "true", it treats the second manifest as a canary shadow and distributes traffic according to canary-weight:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ingress-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "20"
spec:
tls:
- hosts:
- shop.example.com
secretName: shop-tls-secret
rules:
- host: shop.example.com
http:
paths:
- path: /api/v1/orders
pathType: Prefix
backend:
service:
name: order-svc-v2
port: { number: 8080 }
This workaround carries obvious operational risk: when the primary Ingress’s TLS certificate or hostname changes, the canary shadow is easily missed in the update, causing outages. Moreover, regex capture groups like rewrite-target: /$2 are notoriously brittle and prone to subtle path-matching bugs.
After: Decoupled Refactoring with Gateway API
With the Gateway API, every configuration maps cleanly to the principle of “whoever manages it declares it.”
NOTE
The Gateway API is not an in-tree Kubernetes resource; it is distributed independently as CRDs. This is an intentional architectural decision: because Ingress was baked into Kubernetes core, its evolution was tethered to Kubernetes release cycles. Distributing the Gateway API as CRDs enables SIG-Network to iterate independently. You must install the standard CRDs before use (either via official releases or bundled automatically with your controller).
Step 1: Platform / SRE Team Defines the Gateway (gateway.yaml)
The SRE team declares only the listeners, TLS certificates, and namespace label selectors permitted to attach routes. This leverages a two-way handshake model: the Gateway uses allowedRoutes to restrict attachments to namespaces labeled app.kubernetes.io/part-of: shop, requiring application teams to label their namespaces accordingly:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: external-gateway
namespace: infra-gateway
spec:
gatewayClassName: envoy-gateway
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: "shop.example.com"
tls:
mode: Terminate
certificateRefs:
- name: shop-tls-secret
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
app.kubernetes.io/part-of: shop
Step 2: Frontend Team Defines the Root Route (frontend-route.yaml)
Frontend engineers maintain their static asset routing within their dedicated namespace:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: frontend-route
namespace: frontend
spec:
parentRefs:
- name: external-gateway
namespace: infra-gateway
hostnames:
- "shop.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: frontend-svc
port: 80
Step 3: User Service Team Defines Path Rewriting (user-route.yaml)
No cryptic regular expressions—just the standardized URLRewrite filter:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: user-route
namespace: user-app
spec:
parentRefs:
- name: external-gateway
namespace: infra-gateway
hostnames:
- "shop.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api/v1/users
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /users
backendRefs:
- name: user-svc
port: 8080
Step 4: Order Service Team Configures Canary Release (order-route.yaml)
No second shadow YAML required: traffic weights are declared directly within a single rule:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: order-route
namespace: order-app
spec:
parentRefs:
- name: external-gateway
namespace: infra-gateway
hostnames:
- "shop.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api/v1/orders
backendRefs:
- name: order-svc-v1
port: 8080
weight: 80
- name: order-svc-v2
port: 8080
weight: 20
NOTE
Multi-Route Conflict Precedence: All three routes above declare the same hostname shop.example.com, and the frontend route matches the root path /. The Gateway API specification establishes deterministic Longest Prefix Match and rule precedence. Consequently, /api/v1/orders and /api/v1/users are each matched precisely to their own service—completely eliminating the override risk of undefined merge ordering across multiple YAML manifests that characterized the Ingress era.
Migration Comparison Table
The following table summarizes the paradigm shift from Ingress to the Gateway API:
| Use Case | Traditional Ingress (e.g., NGINX) | Gateway API Standard | Architectural & Operational Benefits |
|---|---|---|---|
| Ports & TLS Certificates | Redundantly declared across manifests in spec.tls | Centralized in Gateway.spec.listeners under platform control | Strict security boundaries; app developers never touch Secrets |
| URL Rewriting | rewrite-target annotation paired with regex captures | filters.type: URLRewrite with ReplacePrefixMatch | Declarative, intuitive syntax strictly validated by the API schema |
| Canary Traffic Splitting | Requires a duplicate shadow Ingress annotated with canary: "true" | Direct weight declaration within rules[].backendRefs | Single source of truth; zero shadow manifests to desync |
| Cross-Team & Cross-Namespace | Limited to routing Services in the same Namespace by default | Gateway defines allowedRoutes; Routes bind across namespaces | Native multi-tenancy for microservices with clear RBAC isolation |
| Header Matching | Dependent on proprietary controller annotations | First-class support via rules[].matches[].headers | Native support for A/B testing and advanced canary criteria |
| Redirects | Proprietary annotations (e.g., permanent-redirect) | Standardized filters.type: RequestRedirect | Consistent syntax across all conforming controllers |
| Observability & Diagnostics | Relies on controller container logs; no native status feedback | Rich built-in status.conditions (e.g., Accepted, Programmed) | Instant declarative feedback without grepping centralized controller logs |
Official Migration Tooling: ingress2gateway
If your existing cluster has accumulated a large number of Ingress resources, rewriting them by hand is both time-consuming and error-prone. Kubernetes SIG-Network maintains an official automated conversion utility: ingress2gateway.
Key Tool Capabilities
- Multi-Provider Support: Automatically translates Ingresses and annotations from popular controllers—including Ingress-NGINX, Kong, Istio, and Traefik—into standard Gateway and HTTPRoute resources.
- Client-Side Translation: Supports offline local file conversion as well as live cluster inspection and streaming output.
Common CLI Examples
# 1. Install the CLI tool (via Go or GitHub Releases)
go install github.com/kubernetes-sigs/ingress2gateway@latest
# 2. Translate a single Ingress manifest and preview Gateway API resources
ingress2gateway print --providers ingress-nginx --input-file shop-ingress-main.yaml
# 3. Batch convert all Ingresses in the default namespace from a live cluster
ingress2gateway print --providers ingress-nginx --namespace default > migrated-gateway.yaml
NOTE
ingress2gateway automatically handles over 80% of standard routing, TLS, and basic Rewrite/Redirect rules; esoteric Lua scripts or deeply customized vendor plugins, however, still require manual verification and canary testing.
Evolution and Production Readiness
Following years of real-world battle-testing across the cloud-native community, the Gateway API officially reached General Availability (GA) with v1.0.0 in late 2023, establishing GatewayClass, Gateway, and HTTPRoute as backward-compatible, stable specifications.
The subsequent v1.1 release promoted GRPCRoute to GA and introduced session persistence as an experimental feature, while backend TLS verification (BackendTLSPolicy) only reached GA in v1.4.
Current Landscape
In today’s Kubernetes ecosystem (2026), the Gateway API is unquestionably production-ready. Having been validated across multiple minor releases, it has been adopted as a first-class standard by major cloud platforms and open-source networking projects alike. It is no longer an experimental initiative, but the current industry standard steadily superseding Ingress.
Recommended Implementations and Controllers
Under the Gateway API architecture, Kubernetes defines the API specification (CRDs), while the actual data-plane packet forwarding is executed by a Gateway Controller. When selecting an implementation for your infrastructure, consider these community-tested options:
Key Open-Source Implementations
- The Standard-Bearer for General-Purpose Architectures: Envoy Gateway Initiated by the CNCF and built by community forces from the Envoy project, Tetrate, and VMware, Envoy Gateway is designed from the ground up around the Gateway API specification—free of legacy Ingress technical debt. Its data plane runs on the battle-hardened Envoy Proxy, combining turnkey deployment with enterprise-grade performance. It is the gold standard for self-managed clusters and modern cloud-native architectures.
- The Edge and Lightweight Choice: Traefik Developed by Traefik Labs in Go, Traefik has long served as the default ingress component in Rancher k3s. Its single-binary architecture ensures a minimal memory footprint, and Traefik v3 brings native Gateway API compatibility while seamlessly coexisting with legacy Ingresses in the same cluster—making it a stellar fit for homelabs and edge computing.
TIP
Guidance for Existing Ingress-NGINX Users: The kubernetes/ingress-nginx project was officially retired by Kubernetes, with maintenance halted since March 2026, and has no direct in-place upgrade path to the Gateway API. When evaluating migrations, consider either CNCF’s flagship Envoy Gateway or F5’s official NGINX Gateway Fabric.
Pragmatic Adoption Guidelines (The KISS Principle)
- Greenfield Projects & New Clusters: Adopt the Gateway API directly. Starting with Envoy Gateway or a managed cloud controller is the recommended default.
- Stable Existing Ingress Deployments: Do not rush into an immediate rewrite. Kubernetes maintains an ongoing commitment to supporting the Ingress specification. Favor an incremental transition: deploy new services on Gateway API while leaving legacy routes untouched. When cross-team RBAC friction arises or advanced traffic routing is required, leverage
ingress2gatewayfor a smooth, progressive cutover.
Conclusion
The core value of the Gateway API lies in restoring traffic governance to a modern declarative architecture grounded in “clear role boundaries and strong schema validation.”
Trading the unstructured string annotations of annotation hell for API Server-validated routing filters and cross-namespace authorization boundaries measurably reduces production configuration mistakes and eliminates ticketing friction between teams.
Whether you are deploying greenfield clusters or executing a gradual dual-track migration, mastering this next-generation standard is an essential step toward higher platform engineering maturity.