Gateway API: The Modern Replacement for Ingress#
The Ingress resource has been the standard way to expose HTTP services in Kubernetes since the early days. It works, but it has fundamental limitations: it only supports HTTP, its routing capabilities are minimal (host and path matching only), and every controller extends it through non-standard annotations that are not portable. Gateway API is the official successor – a set of purpose-built resources that provide richer routing, protocol support beyond HTTP, and a role-oriented design that cleanly separates infrastructure concerns from application concerns.
Gateway API reached General Availability (v1.0) for its core resources in late 2023. It is not experimental. It is the recommended path forward for new deployments, and the community actively maintains it alongside all major ingress controller implementations.
Why Gateway API Over Ingress#
The practical limitations of Ingress that Gateway API addresses:
Protocol support. Ingress only handles HTTP/HTTPS. Gateway API adds GRPCRoute, TCPRoute, UDPRoute, and TLSRoute as first-class resources. You no longer need a separate mechanism for non-HTTP traffic.
Routing expressiveness. Ingress supports host-based and path-based routing. Gateway API’s HTTPRoute supports header matching, query parameter matching, HTTP method matching, request/response header modification, URL rewrites, request redirects, request mirroring, and weighted traffic splitting – all through standard fields, not annotations.
Portability. Ingress annotations are controller-specific. nginx.ingress.kubernetes.io/rewrite-target does not work with Traefik, HAProxy, or any other controller. Gateway API features are defined in the spec itself, so an HTTPRoute with header-based matching works the same way across any conformant implementation.
Role separation. Ingress puts everything in one resource: the listener configuration, TLS certificates, and routing rules. Gateway API splits these across GatewayClass, Gateway, and Route resources, aligning with organizational roles.
Core Resources#
GatewayClass#
GatewayClass is cluster-scoped and defines the controller that manages Gateways of this class. It is analogous to IngressClass. The infrastructure provider (or cluster administrator) creates it.
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: production-gateway
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controllerA cluster can have multiple GatewayClasses – for example, one backed by Envoy for production traffic and another backed by Nginx for internal services.
Gateway#
Gateway is namespace-scoped and represents an actual load balancer or proxy instance. The cluster operator creates it, specifying which ports to listen on, which protocols to accept, and which TLS certificates to use.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: web-gateway
namespace: infrastructure
spec:
gatewayClassName: production-gateway
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: wildcard-tls
namespace: infrastructure
allowedRoutes:
namespaces:
from: All
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: AllThe allowedRoutes.namespaces field controls which namespaces can attach routes to this Gateway. The options are All, Same (only the Gateway’s namespace), or Selector (namespaces matching a label selector). This is a critical security boundary – it prevents arbitrary namespaces from claiming routes on your production load balancer.
HTTPRoute#
HTTPRoute is namespace-scoped and defines routing rules for HTTP traffic. Application developers create these to route traffic to their services. An HTTPRoute attaches to one or more Gateways via parentRefs.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-routes
namespace: app-team
spec:
parentRefs:
- name: web-gateway
namespace: infrastructure
hostnames:
- "api.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /v2/users
headers:
- name: X-API-Version
value: "2"
backendRefs:
- name: users-v2
port: 8080
- matches:
- path:
type: PathPrefix
value: /v1
backendRefs:
- name: api-v1
port: 8080GRPCRoute#
GRPCRoute provides native gRPC routing, including matching on gRPC service and method names.
apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
name: grpc-routes
namespace: app-team
spec:
parentRefs:
- name: web-gateway
namespace: infrastructure
rules:
- matches:
- method:
service: myapp.UserService
method: GetUser
backendRefs:
- name: user-grpc
port: 9090TCPRoute, UDPRoute, TLSRoute#
These are experimental (alpha) resources for Layer 4 routing. TCPRoute routes raw TCP connections, UDPRoute handles UDP, and TLSRoute routes based on SNI (Server Name Indication) without terminating TLS.
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TCPRoute
metadata:
name: postgres-route
spec:
parentRefs:
- name: tcp-gateway
rules:
- backendRefs:
- name: postgres
port: 5432Role-Oriented Design#
The separation of resources maps to organizational roles:
Infrastructure provider manages GatewayClass. This is the team that installs and maintains the controller (Envoy, Nginx, Istio, Cilium). They define what types of gateways are available.
Cluster operator manages Gateway. This is the platform team that provisions load balancers, manages TLS certificates, controls which namespaces can create routes, and sets resource constraints. They decide what ports are open and what certificates are used.
Application developer manages HTTPRoute. This is the team deploying applications. They define how traffic reaches their service – which hostnames, which paths, which headers. They do not need access to TLS certificates or load balancer configuration.
With Ingress, all three concerns were in one resource, requiring application developers to have access to TLS certificates and load balancer configuration. Gateway API’s separation means an application developer can create an HTTPRoute in their namespace without touching infrastructure.
HTTPRoute Features in Detail#
Path Matching#
matches:
- path:
type: Exact
value: /api/v1/health
- path:
type: PathPrefix
value: /api/v1/
- path:
type: RegularExpression
value: "/api/v[0-9]+/users/[0-9]+"Header and Query Parameter Matching#
matches:
- headers:
- name: X-Canary
value: "true"
queryParams:
- name: debug
value: "1"Request and Response Header Modification#
rules:
- matches:
- path:
type: PathPrefix
value: /api
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: X-Request-Source
value: gateway
remove:
- X-Internal-Header
- type: ResponseHeaderModifier
responseHeaderModifier:
set:
- name: X-Frame-Options
value: DENY
backendRefs:
- name: api-service
port: 8080URL Rewrites#
filters:
- type: URLRewrite
urlRewrite:
hostname: internal-api.default.svc.cluster.local
path:
type: ReplacePrefixMatch
replacePrefixMatch: /internalRedirects#
filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301Traffic Splitting#
Gateway API provides built-in weighted traffic splitting through backendRefs, enabling canary deployments without additional tools like Argo Rollouts or Flagger.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: canary-route
spec:
parentRefs:
- name: web-gateway
namespace: infrastructure
hostnames:
- "app.example.com"
rules:
- backendRefs:
- name: app-v1
port: 8080
weight: 90
- name: app-v2
port: 8080
weight: 10This sends 90% of traffic to v1 and 10% to v2. Adjust the weights as confidence in v2 increases. Combined with header matching, you can also route specific users (by header, cookie, or query param) to the canary while keeping everyone else on the stable version.
rules:
# Canary users go to v2
- matches:
- headers:
- name: X-Canary
value: "true"
backendRefs:
- name: app-v2
port: 8080
# Everyone else goes to v1
- backendRefs:
- name: app-v1
port: 8080TLS Configuration#
TLS is managed at the Gateway level, not in the route. This separation ensures application developers do not need access to TLS certificates.
TLS Termination#
The Gateway terminates TLS and forwards plaintext to the backend:
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: tls-certTLS Passthrough#
The Gateway forwards the encrypted connection directly to the backend, which handles TLS termination:
listeners:
- name: tls-passthrough
protocol: TLS
port: 443
tls:
mode: PassthroughWith passthrough, the Gateway routes based on SNI (TLSRoute) since it cannot inspect the encrypted HTTP content.
Implementations#
Gateway API is a specification. You need a controller that implements it. Major implementations:
- Envoy Gateway: The reference implementation backed by the Envoy proxy. Full conformance, active development.
- Istio Gateway API: Istio natively supports Gateway API resources as of Istio 1.16+. Provides the full Istio feature set (mTLS, observability) alongside Gateway API routing.
- Cilium: eBPF-based implementation, highly performant. Good for clusters already using Cilium as their CNI.
- Nginx Gateway Fabric: The official Nginx implementation. Currently supports core HTTPRoute features.
- Traefik: Supports Gateway API alongside its native IngressRoute resources.
- Kong: Kong Gateway supports Gateway API as an alternative to its KongIngress CRD.
- HAProxy: HAProxy Kubernetes Ingress Controller supports Gateway API.
- AWS Gateway API Controller: Maps Gateway API resources to AWS Application Load Balancer (ALB) and Network Load Balancer (NLB).
Not all implementations support all features. The Gateway API project maintains a conformance test suite, and implementations declare which conformance profiles they support (Core, Extended, Implementation-specific). Check your chosen controller’s documentation for its current conformance level.
Migration from Ingress#
Gateway API is backwards-compatible in concept. The mapping is:
- Ingress annotations for listener config (ports, TLS) move to the Gateway resource
- Ingress rules (host matching, path matching, backend references) move to HTTPRoute
- IngressClass maps to GatewayClass
You can run Ingress and Gateway API side by side during migration. Most controllers support both simultaneously. A practical migration path:
- Install the Gateway API CRDs and your chosen controller
- Create a GatewayClass and Gateway alongside the existing Ingress controller
- Migrate services one at a time: create an HTTPRoute, verify traffic flows, then remove the corresponding Ingress rule
- Once all Ingress rules are migrated, decommission the old Ingress controller
# Install Gateway API CRDs
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
# Verify the CRDs are installed
kubectl get crd | grep gateway.networking.k8s.ioCurrent Status#
As of early 2026:
- HTTPRoute: GA (v1). Stable, production-ready.
- GRPCRoute: GA (v1). Stable, production-ready.
- Gateway and GatewayClass: GA (v1). Stable.
- TCPRoute, UDPRoute, TLSRoute: Experimental (v1alpha2). API may change.
- ReferenceGrant: GA. Controls cross-namespace references (allowing an HTTPRoute in namespace A to reference a backend in namespace B).
Common Gotchas#
Routes not attaching to Gateway. The Gateway must explicitly allow routes from the route’s namespace via allowedRoutes.namespaces. If set to Same and the route is in a different namespace, it will not attach. Check the Gateway status conditions for attachment errors:
kubectl get gateway <name> -n <ns> -o jsonpath='{.status.listeners}' | jq .Cross-namespace backend references. By default, an HTTPRoute cannot reference a Service in a different namespace. You need a ReferenceGrant in the target namespace:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-from-app-team
namespace: backend-services
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: app-team
to:
- group: ""
kind: ServiceFeature support varies by implementation. An HTTPRoute with RegularExpression path matching works with Envoy Gateway but may not work with every controller. Always check the conformance profile of your controller before relying on Extended features. The status field on HTTPRoute resources will show whether the controller accepted the route and any conditions that indicate unsupported features.
Forgetting to install the CRDs. Gateway API resources are not built into Kubernetes. You must install the CRDs separately. Without them, kubectl apply on Gateway API resources will return “no matches for kind.”