Gateway API is an add-on, and its version must match Cilium¶
Gateway API is not part of Kubernetes and nothing installs it by default —
not Talos, and not Cilium unless gatewayAPI.enabled is set. It comes from
terraform/talos-bootstrap, which applies the upstream experimental-install.yaml
for a pinned release before the Cilium chart; a Renovate customManager bumps
gateway_api_version against GitHub releases.
Pin the version to what the controller wants, not to whatever is newest-looking. Cilium 1.20 requires Gateway API v1.6.1 at a minimum, and a controller whose informers watch a kind at a version the cluster does not serve fails with "the server could not find the requested resource" — indistinguishable from the CRDs being absent. The provider then never syncs, so:
GatewayClass Accepted=Unknown "Waiting for controller"
Gateway Programmed=Unknown
HTTPRoute status: <empty>
and external-dns, which reads targets from an HTTPRoute's status.parents,
silently emits nothing.
Use the experimental channel, for three reasons:
TLSRoutein the v1.6 standard channel no longer shipsv1alpha2. Install that one over an existingTLSRouteand the apiserver cannot read the records out of etcd — the objects simply disappear. Upstream flags this as an upgrade action for Cilium 1.20.- The
ExternalAuthHTTPRoute filter field exists only in experimental. See Routing. ListenerSet,TCPRouteandUDPRouteare separate CRDs, and Cilium disables the corresponding feature for each one that is missing.
The channel is a strict superset of standard, so nothing is lost by using it.
Cilium reads the CRDs at startup, so tofu owns them¶
Cilium is the CNI: nothing else runs until it does, which puts it ahead of Flux and
therefore ahead of anything Flux could apply. That ordering is why the CRDs cannot
live in a Flux layer. Cilium disables Gateway API support for every one of its CRDs
missing at startup, and the chart's gatewayAPI.gatewayClass.create: auto decides
at template time by looking for the GatewayClass CRD — so CRDs that arrive two
minutes later leave neither a controller nor a GatewayClass, with
enable-gateway-api=true sitting in the ConfigMap the whole time. The Gateway
then waits on a class that does not exist:
$ kubectl get gatewayclass
(nothing)
$ kubectl -n gateway get gateway
Accepted=Unknown reason=Pending msg="Waiting for controller"
Nothing binds :443 and every external request is refused at the TCP level, which
reads as "the gateway was never installed" rather than as an ordering problem.
kubectl_manifest with server_side_apply does the applying, because these are
13 CRDs of 24 000 lines — client-side apply stores the whole object in an
annotation and blows the size limit. gatewayClass.create stays auto: forcing it
true renders a GatewayClass unconditionally, which is a hard Helm failure on an
unknown kind if the CRDs ever are missing.