CertGrid CertGrid
Concepts·Kubernetes and Cloud Native Associate

Gateway API and GatewayClass

The Gateway API is not in Kubernetes - it arrives as CRDs you install. This shows what that means concretely: before, the API group does not exist; after, the kinds are there and every object you create sits at Accepted=Unknown with no address, because CRDs are a vocabulary and not an implementation.

Networking and Discovery Guide 21 of 46 Intermediate

Written against the versions above. Gateway API v1.4.0 standard channel. It is developed by SIG-Network inside the Kubernetes project - which is why its group is gateway.networking.k8s.io and not a vendor domain - but it ships separately from the cluster and always has to be installed.

A cluster with no Gateway API installed at the start, which is the normal state and the point of step 1.
Server NameIP AddressOSRolesCPURAMHDD
CKA1001192.168.0.175Ubuntu 26.04 LTSControl Plane Node2 Core4 GB50 GB
CKA1001-NODE01192.168.0.176Ubuntu 26.04 LTSWorker Node2 Core4 GB50 GB
CKA1001-NODE02192.168.0.177Ubuntu 26.04 LTSWorker Node2 Core4 GB50 GB
CKA1001-NODE03192.168.0.178Ubuntu 26.04 LTSWorker Node2 Core4 GB50 GB

Before you start

  1. The API is not in your cluster

    Start by establishing what is not there. Count the Gateway API CRDs:

    0

    And ask the API server for the group:

    NAME   SHORTNAMES   APIVERSION   NAMESPACED   KIND

    A header and nothing under it. The Gateway API is not part of Kubernetes. That is the fact this whole guide exists to fix in people's heads. Ingress is built in - networking.k8s.io/v1, present on every cluster. Gateway API is a set of CustomResourceDefinitions that somebody has to install, and until they do, kubectl apply of a Gateway fails outright because the kind does not exist.

    It is still a Kubernetes project - SIG-Network develops it, which is why the group is gateway.networking.k8s.io rather than a company's domain. Being an official project and being installed are different things.

    bash Example session
    kubectl get crd -o name 2>/dev/null | grep -c gateway.networking.k8s.io || echo 000kubectl api-resources --api-group=gateway.networking.k8s.io 2>&1 | head -3NAME   SHORTNAMES   APIVERSION   NAMESPACED   KIND

    Expected resultZero CRDs, and an api-resources listing with a header and no rows.

    Success conditionYou can say whether Gateway API ships with Kubernetes.

  2. Installing the CRDs adds vocabulary, not behaviour

    Apply the standard install and the kinds appear:

    NAME                 SHORTNAMES   APIVERSION                     NAMESPACED   KIND
    backendtlspolicies   btlspolicy   gateway.networking.k8s.io/v1   true         BackendTLSPolicy
    gatewayclasses       gc           gateway.networking.k8s.io/v1   false        GatewayClass
    gateways             gtw          gateway.networking.k8s.io/v1   true         Gateway

    Note GatewayClass is NAMESPACED false - it is cluster-scoped, like IngressClass and StorageClass. Anything that names an implementation tends to be.

    Now the part that matters:

    No resources found

    There are no GatewayClasses. The CRDs define what a Gateway *is*; they say nothing about who acts on one. A CRD is a schema the API server will now store and validate - a noun added to the cluster's vocabulary. Behaviour comes from a controller, and installing the API installed none.

    bash Example session
    sleep 6; kubectl api-resources --api-group=gateway.networking.k8s.ioNAME                 SHORTNAMES   APIVERSION                          NAMESPACED   KINDbackendtlspolicies   btlspolicy   gateway.networking.k8s.io/v1        true         BackendTLSPolicygatewayclasses       gc           gateway.networking.k8s.io/v1        false        GatewayClassgateways             gtw          gateway.networking.k8s.io/v1        true         Gatewaykubectl get gatewayclass --no-headers 2>&1 | head -3No resources found

    Expected resultThe Gateway API kinds present, and no GatewayClass at all.

    Success conditionYou can state the difference between a CRD and a controller.

  3. An object that validates and does nothing

    Create a Gateway naming a class nothing implements. It is accepted - the schema is valid, so the API server stores it:

    orphan   nothing-installed         Unknown   12s
    conditions=Accepted=Unknown(Pending) Programmed=Unknown(Pending)

    Read those two conditions carefully, because Unknown is doing real work here. Not False. False would be a controller saying no. Unknown with reason Pending is the API's way of saying *nobody has answered at all* - the status was initialised and no controller has ever looked at it.

    That distinction is the whole diagnostic. A False condition means read the message, because something rejected your configuration. Unknown(Pending) forever means no controller is watching, and no amount of fixing the YAML will change it.

    The same happens with a properly named class once a controller's manifests are applied but its Pod has not become ready - the Gateway still reports nothing:

    address= listeners=

    No address to send traffic to, and no listeners reporting. Programmed is the condition that says actual dataplane configuration happened; until it is True, the Gateway is a document.

    bash Example session
    sleep 12; kubectl get gateway orphan --no-headersorphan   nothing-installed         Unknown   12skubectl get gateway orphan -o jsonpath='conditions={range .status.conditions[*]}{.type}={.status}({.reason}) {end}{"\n"}'conditions=Accepted=Unknown(Pending) Programmed=Unknown(Pending) sleep 30; kubectl get gateway shop -n gw --no-headersshop   nginx         Unknown   30skubectl get gateway shop -n gw -o jsonpath='address={.status.addresses[0].value} listeners={range .status.listeners[*]}{.name}:attached={.attachedRoutes}{" "}{end}{"\n"}'address= listeners=

    Expected resultA stored Gateway with both conditions Unknown(Pending), no address and no listeners.

    Success conditionYou can tell Unknown(Pending) from False, and say what each implies.

  4. A route attached to nothing

    An HTTPRoute is the object that actually carries rules - paths, hostnames, backends - and it attaches itself to a Gateway through parentRefs. Creating one also succeeds:

    shop-routes   ["shop.cg.test"]   20s

    The hostname is recorded. And the Gateway's view of it:

    attachedRoutes=

    Empty. The route exists, names a real Gateway, and is attached to nothing - because attachment is something the *controller* records, and there isn't one working. attachedRoutes on the Gateway's listener is the field to read when a route appears to be ignored; a number there means the controller saw it.

    That is the shape of the whole API and the reason it is worth learning even before you use it. Ingress puts routing rules and implementation-specific behaviour in one object, with a pile of vendor annotations. Gateway API splits them by role: GatewayClass is the implementation, chosen by a cluster administrator; Gateway is a listener, owned by whoever runs the platform; HTTPRoute is a rule, owned by the team that owns the service. Weighted splits between backends - a canary - are expressed in the API itself rather than in an annotation, which is the concrete thing Ingress cannot do portably.

    Which is also why the honest close to this guide is that nothing was routed. The controller never became ready, so the objects stayed exactly as described here. For KCNA that is the more useful outcome anyway: install the API, and you have the nouns; install a controller, and you have the behaviour.

    bash Example session
    sleep 20; kubectl get httproute shop-routes -n gw --no-headersshop-routes   ["shop.cg.test"]   20skubectl get gateway shop -n gw -o jsonpath='attachedRoutes={.status.listeners[0].attachedRoutes}{"\n"}'attachedRoutes=kubectl get svc -n gw --no-headers | awk '{print $1, $2, $4}'blue ClusterIP <none>green ClusterIP <none>

    Expected resultAn HTTPRoute stored with its hostname, and a Gateway reporting no attached routes.

    Success conditionYou know which field tells you whether a controller has seen your route.

Troubleshooting

Official sources