<- Back to notes
Current Kubernetes Intermediat

A Brief introduction to Gateway API

A comprehensive view of Kubernetes GatewayAPI definition and implementation details

Updated May 28, 2026 15 min read
k8skubernetesingressgateway-api

1) What Gateway API is, in one sentence

Gateway API is Kubernetes' next-generation traffic management API for north-south and, increasingly, east-west traffic. It replaces the "one big Ingress object plus controller-specific annotations" model with a role-oriented object model: GatewayClass for platform-wide implementation selection, Gateway for concrete entry points/listeners, and Route resources such as HTTPRoute, GRPCRoute, TLSRoute, TCPRoute, and UDPRoute for application traffic rules.

2) The architectural model

Architecturally, Gateway API splits responsibilities across three layers:

  1. API layer: the CRDs (GatewayClass, Gateway, HTTPRoute, etc.).
  2. Controller layer: the implementation that watches those CRDs and translates them into concrete proxy/load-balancer configuration.
  3. Data plane: the actual traffic engine that receives packets and enforces routing and policy. Depending on implementation, that data plane may be Envoy, an implementation-specific proxy, or a tighter integration with the CNI datapath.

The key mental model is:

  • GatewayClass = which controller/implementation owns this
  • Gateway = where and how traffic enters
  • Route = how traffic is matched and forwarded
  • optional policy and security resources = how traffic is modified, authenticated, encrypted, or restricted

Role orientation

A major design goal is to let different teams own different parts safely. The platform team can own the GatewayClass and often the Gateway; application teams can own HTTPRoute or GRPCRoute. Route attachment is intentionally bidirectional: a Route must point at a Gateway, and the Gateway listener must allow that Route kind/namespace to attach. This is one of the biggest architectural differences from Ingress.

That gives you a built-in trust model for multi-tenancy. A platform team can expose a shared listener, but app teams still control whether their app is exposed. The Gateway owner can constrain which namespaces and kinds may attach, but cannot force a Route into existence.

3) The core resources, technically

GatewayClass

GatewayClass is cluster-scoped and chooses the controller through spec.controllerName. Every controller advertises its own name and reconciles only the GatewayClass objects that match it. A Gateway references exactly one GatewayClass, so this is the top-level binding between the Kubernetes API and the implementation.

In practice, this is where platform teams standardize behavior. With some implementations, GatewayClass can also point at implementation-specific parameters or extension resources. With Envoy Gateway, for example, Gateway API resources are the standard interface, and Envoy Gateway-specific CRDs provide extension points for advanced behavior.

Gateway

A Gateway is the concrete "front door". It defines one or more listeners: each listener usually specifies a protocol, port, optional hostname, TLS mode, and which Route kinds/namespaces may attach. It is a request for infrastructure capable of receiving traffic and translating it to Kubernetes backends. The actual infrastructure behind that request depends on the controller: on bare metal it may result in a LoadBalancer Service plus proxy pods, host-network exposure, a NodePort pattern, or an implementation-specific construct.

Think of a listener as "a socket with policy." For example:

  • HTTPS :443 for app.example.com
  • HTTP :80 with redirect behavior
  • TLS :443 in passthrough mode for SNI-based routing
  • GRPC :443 or HTTP/2 traffic under a specific host

Routes

Routes describe traffic matching and forwarding.

  • HTTPRoute is for HTTP and terminated HTTPS, including path/header/query matching, header manipulation, redirects, rewrites, mirroring, weighting, retries/timeouts depending on feature support.
  • GRPCRoute is the idiomatic gRPC route type; support for GRPCRoute is GA in the Standard channel since v1.1.0.
  • TLSRoute is for routing encrypted TLS streams using SNI without needing HTTP awareness. It is GA in the Standard channel since Gateway API v1.5.0.
  • TCPRoute and UDPRoute remain in the Experimental channel.

A Route attaches to a Gateway by naming it in parentRefs. The controller then checks whether the listener allows that Route kind and namespace, whether hostnames intersect legally, whether referenced backends exist, and whether all referenced objects are valid. Route attachment is evaluated per parentRef, so the same Route can be accepted by one parent and rejected by another.

ReferenceGrant

ReferenceGrant is a security object for cross-namespace references. If a Route in namespace team-a wants to send traffic to a Service in shared-backends, or a Gateway wants to use a Secret from a different namespace, the target namespace must explicitly allow that reference using a ReferenceGrant. Without it, the reference is invalid.

This matters a lot operationally. Gateway API deliberately avoids implicit cross-namespace access because that creates confused-deputy and privilege-escalation risks.

4) Release channels and why they matter in production

Gateway API has Standard and Experimental release channels. The Standard channel contains GA or stable-enough features with backward-compatibility guarantees inside that channel, while the Experimental channel contains alpha resources and newly introduced fields that may change more aggressively.

For production on a bare-metal cluster, the safest default is:

  • install the Standard CRDs
  • use Standard resources/fields
  • choose a controller with a recent conformance report
  • use implementation-specific extensions only where the standard API is genuinely insufficient

As of the current project docs, Gateway, GatewayClass, and HTTPRoute have long been GA; GRPCRoute is Standard since v1.1; TLSRoute is Standard since v1.5; TCPRoute and UDPRoute are still Experimental.

5) How reconciliation works end to end

Here is the control-flow in practice:

  1. You apply the Gateway API CRDs.
  2. You install a controller implementation.
  3. The controller creates or expects one or more GatewayClass objects.
  4. You create a Gateway that references the chosen GatewayClass.
  5. App teams create Route resources that point to the Gateway.
  6. The controller validates attachment rules and referenced backends.
  7. The controller programs the dataplane.
  8. The dataplane starts serving traffic.
  9. Status fields and Conditions tell you whether all of that succeeded.

The important point is that the Kubernetes API objects are declarative intent, not the dataplane itself. The controller is the translator, and the dataplane is the executor. That separation is why different implementations can all use the same API but behave differently operationally.

6) Status and conditions: the most important debugging surface

Gateway API leans heavily on Conditions so you do not have to jump straight to controller logs. The most important portable conditions are:

  • Accepted: the object is syntactically/semantically valid and accepted by the controller.
  • ResolvedRefs: referenced resources are valid and resolvable.
  • Programmed: the config has been translated and sent to the dataplane.

On a healthy Gateway, you normally want to see Accepted=True, Programmed=True, and a populated status.addresses. On a Route, you want to inspect status.parents for per-parent conditions and ensure ResolvedRefs=True and Accepted=True. The docs also call out checking observedGeneration against metadata.generation so you know you are not reading stale status.

That status-first troubleshooting style is one of the best practical improvements over old Ingress workflows.

7) How traffic actually flows

For a typical HTTP case:

  1. A client connects to an IP and port exposed by the implementation.
  2. The dataplane receives the connection.
  3. The listener is chosen by port/protocol and often hostname.
  4. The attached HTTPRoute rules are evaluated.
  5. The matching backend Service and port are selected.
  6. The implementation sends the request to the chosen Pod endpoints, usually via the Service abstraction and EndpointSlices.

For TLS termination:

  • the Gateway listener holds the certificate reference
  • TLS is terminated at the dataplane
  • decrypted HTTP traffic is then matched by HTTPRoute or GRPCRoute rules

For TLS passthrough:

  • the dataplane reads just enough of the TLS handshake to inspect SNI
  • TLSRoute chooses the backend based on SNI
  • the encrypted stream continues to the backend without edge termination

For gRPC:

  • use GRPCRoute when your implementation supports it well and you want a more protocol-native object
  • otherwise some implementations also carry gRPC over HTTP/2 paths/hostnames through HTTPRoute, but GRPCRoute is the idiomatic API surface

8) Bare-metal reality: Gateway API does not magically give you an external IP

On a VM or bare-metal cluster, Gateway API gives you the API model, but you still need a way to make the dataplane reachable from outside the cluster. In practice, that usually means one of these patterns:

  • a LoadBalancer Service with MetalLB
  • a LoadBalancer Service with Cilium LB IPAM
  • NodePort
  • host-network exposure on nodes
  • integration with an external hardware/software load balancer

This is where many first deployments get confused: Gateway API is not itself an IPAM or ARP/BGP advertisement system. On bare metal, you need another component to advertise or assign the address that the Gateway dataplane will use.

9) Implementation choices in your environment

Option A: Cilium as the Gateway API implementation

Cilium has a conformant Gateway API implementation and integrates its ingress/gateway path tightly with the CNI. Its docs describe traffic arriving through a LoadBalancer, NodePort, or host-network exposure, then being intercepted by eBPF and forwarded to Envoy using TPROXY. That is quite different from "just install another independent ingress controller deployment."

Operationally, that has consequences:

  • client IP visibility behaves differently than many traditional controllers
  • Cilium policy can apply to ingress/gateway traffic
  • you often reason about both the gateway proxy and the Cilium datapath together
  • Cilium's own LB IPAM can provide external IP allocation on bare metal if you choose that path

A subtle but important Cilium point: for Cilium Gateway/Ingress traffic, source IP visibility generally works without needing externalTrafficPolicy: Local in the same way older controllers often required, because traffic is intercepted and redirected differently in the datapath.

Option B: Calico + Envoy Gateway / Calico Ingress Gateway

Calico's current Gateway story is centered on Envoy Gateway-based ingress/gateway support. Calico docs explicitly describe the ingress gateway as based on Envoy Gateway, where Envoy Gateway is the control plane translating Gateway API resources and Envoy Proxy is the data plane.

That means the Calico path is conceptually more like:

  • Gateway API CRDs
  • Envoy Gateway controller
  • Envoy proxy dataplane
  • Calico networking/policy underneath

rather than Cilium's tighter CNI-native interception model.

A practical benefit is that Envoy Gateway is a well-defined upstream implementation with clear extension APIs and deployment modes. A practical tradeoff is that you are now operating a distinct gateway stack in addition to the CNI.

10) A production-safe deployment pattern for bare metal

For a bare-metal platform team, the cleanest production pattern is usually:

  1. Install Gateway API Standard CRDs.
  2. Install one conformant controller for ingress traffic.
  3. Provide external reachability using MetalLB or Cilium LB IPAM.
  4. Create a small number of shared GatewayClass and Gateway objects centrally.
  5. Let app teams manage only HTTPRoute/GRPCRoute in their namespaces.
  6. Restrict cross-namespace use with listener namespace selectors and minimal ReferenceGrants.
  7. Add cert-manager integration for certificates if needed. The implementations page explicitly lists cert-manager as an integration that can generate TLS certificates for Gateway resources through annotations.

Why this pattern works well:

  • the platform owns exposure, addresses, TLS defaults, and blast radius
  • apps own only routing intent
  • rollback is mostly object-based and declarative
  • status conditions are understandable and portable across controllers

11) A minimal configuration, explained

A minimal HTTP deployment usually contains three things:

  • a GatewayClass
  • a Gateway
  • an HTTPRoute

Example:

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: edge
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: public
  namespace: infra-gateway
spec:
  gatewayClassName: edge
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              expose-via-gateway: "true"
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app
  namespace: team-a
spec:
  parentRefs:
    - name: public
      namespace: infra-gateway
  hostnames:
    - app.example.internal
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: app-svc
          port: 8080

What this means in practice:

  • the controller named in GatewayClass owns the stack
  • the shared Gateway opens port 80
  • only namespaces labeled expose-via-gateway=true may attach routes
  • the app team binds HTTPRoute to that Gateway and forwards to app-svc:8080

A production version usually adds:

  • HTTPS listener
  • certificate reference
  • HTTP→HTTPS redirect
  • explicit hostname scoping
  • route filters where supported
  • policy resources or implementation-specific extensions for auth/rate limiting/timeouts
  • observability and resource tuning on the dataplane

12) TLS in practice

TLS in Gateway API is more coherent than in Ingress because certificates live naturally on the Gateway listener, not scattered across app-owned resources. The project's TLS guide covers terminated TLS, passthrough, and cross-namespace certificate refs. If a certificate Secret is in another namespace, that requires ReferenceGrant.

Typical production split:

  • edge TLS termination on the Gateway for web apps
  • TLS passthrough only when the backend must hold the cert and you only need SNI-based forwarding
  • backend TLS with BackendTLSPolicy when the implementation supports it and you want encrypted traffic from gateway to backend Service as well

13) Namespace and tenant design

A good multi-tenant design on bare metal is:

  • gateway-system or infra-gateway namespace for shared Gateways
  • app namespaces own their Routes
  • allowedRoutes on listeners restrict which namespaces may bind
  • avoid cross-namespace backends unless there is a very strong reason
  • when cross-namespace is necessary, use narrowly scoped ReferenceGrants only in the target namespace

That model aligns with the API's intended separation of duties and prevents app teams from accidentally or deliberately routing to arbitrary services they do not own.

14) Policy and extension model

Gateway API was designed to be extensible. The standard API covers common routing and attachment semantics, and implementations layer in additional CRDs or policy APIs for richer behavior. Envoy Gateway explicitly documents this model with resources such as ClientTrafficPolicy, SecurityPolicy, BackendTrafficPolicy, EnvoyProxy, and EnvoyPatchPolicy, while still using Gateway API as the primary interface.

In practice, the production rule should be:

  • use standard Gateway API wherever it is expressive enough
  • standardize on one implementation's extension APIs only for features you truly need
  • document those implementation-specific dependencies clearly, because portability drops once you rely on them

15) Gateway API for east-west traffic and GAMMA

Gateway API is no longer only about ingress. The project also supports mesh-oriented usage where a Service can act as a parent for an HTTPRoute; this is part of the GAMMA direction. The Gateway API v1.1 release highlighted service mesh support, and Cilium documents GAMMA support where an HTTPRoute binds to a Service instead of a north-south Gateway.

For your question, that is relevant mainly because:

  • Cilium is strong here if you want one model for ingress and service-to-service traffic management.
  • Calico + Envoy Gateway is more naturally thought of as ingress first, with the CNI and gateway remaining more separate operational concerns.

16) Common failure modes on VM/bare metal

The most common practical failures are:

No external address exists. The Gateway may be accepted, but no reachable IP is advertised because you have not deployed MetalLB, Cilium LB IPAM, host networking, or another exposure mechanism.

Gateway accepted, Route not attached. Usually allowedRoutes, namespace restrictions, hostname mismatch, or incorrect parentRefs. Check Route status.parents.

ResolvedRefs=False. Backend Service name/port wrong, cross-namespace backend lacks ReferenceGrant, unsupported protocol, or invalid Secret/certificate reference.

TLS confusion. Using HTTPRoute when you really needed TLSRoute, or expecting passthrough behavior from a terminating listener.

Implementation mismatch with features. A manifest may be valid per the CRD but not supported by your controller profile/version. Conformance is profile-based, and implementations differ in which Extended features they claim.

Policy surprises with Cilium. Because ingress/gateway traffic interacts with the Cilium policy engine and identities, you may need to reason about policy on both sides of the ingress identity path, not just backend pod policy.

17) How I would deploy it in practice on your two likely stacks

If the cluster uses Cilium

I would usually choose:

  • Gateway API Standard CRDs
  • Cilium's Gateway API implementation
  • external exposure via either Cilium LB IPAM or existing BGP/L2 strategy
  • shared Gateways in one infra namespace
  • app-team-owned HTTPRoutes
  • Cilium policy explicitly reviewed for ingress-to-backend flows

This is the cleaner choice when you want the gateway tightly aligned with the network datapath and potentially with GAMMA/service-mesh style traffic management later.

If the cluster uses Calico

I would usually choose:

  • Gateway API Standard CRDs
  • Envoy Gateway or Calico's Envoy Gateway-based ingress gateway
  • external exposure via MetalLB or whatever bare-metal load-balancer path you already operate
  • clear ownership boundaries between gateway stack and CNI/network policy
  • implementation-specific policy only where necessary

This is the cleaner choice when you want a conventional gateway control plane/data plane model and you are comfortable operating Envoy Gateway as a first-class platform component.

18) Production recommendations

For a real platform, my default recommendations would be:

  • use Standard channel CRDs only
  • choose one primary gateway implementation
  • centralize GatewayClass and shared Gateway ownership
  • let app teams own only Routes
  • prefer hostname-scoped listeners and routes
  • keep ReferenceGrant usage rare and explicit
  • use status conditions before logs when troubleshooting
  • standardize external IP strategy early: MetalLB, Cilium LB IPAM, or external LB
  • validate implementation conformance and supported features before using advanced filters/policies
  • keep extension API use deliberate, because portability ends there

19) The shortest "why it matters" summary

Ingress was "one object + annotations + controller magic." Gateway API is "portable intent + explicit attachment + explicit status + cleaner separation of concerns." That makes it much better for platform engineering, multi-tenancy, and long-term maintainability, especially on self-managed clusters where you need to be very clear about who owns exposure, who owns app routing, and how addresses are made reachable.

Copied to clipboard