AI-narrated article. Prose is preserved with pronunciation adjustments; code and image cues refer to the written article.
Gateway API v1.6 TCPRoute and UDPRoute: Accepted Is Not a Traffic Test
This is the AI-narrated article. The prose follows the written post; pronunciation is adjusted, and code and images are announced separately.
Expose DNS over TCP and UDP with explicit listener ownership, a pinned Envoy Gateway configuration, and transport-specific failure diagnosis.
TL;DR: Gateway API v1.6 stabilizes TCPRoute and UDPRoute, but reliable exposure still depends on controller support, installed CRDs, listener allocation, Service protocols and network reachability. Use separate TCP and UDP listeners to avoid optional shared-port assumptions. This article provides a complete DNS configuration with pinned Envoy Gateway and CoreDNS versions, explicit attachment boundaries and a static answer. It explains route precedence, backend permissions, source-address consequences and UDP-only validation. Diagnose the exact transport boundary before changing infrastructure, and distinguish successful reconciliation from proof that the intended application receives traffic.
GA changes the API contract, not the packet path
A DNS gateway answers TCP queries but UDP clients time out. Both route objects exist. The application team sees a networking incident; the platform team sees a successfully reconciled configuration. The missing piece is a transport-specific contract that extends beyond the Route resource.
Gateway API v1.6 moved TCPRoute and UDPRoute into the Standard channel with a v1 API. That makes their configuration surface stable. It does not guarantee that an installed controller supports them, that its Service exposes UDP, or that a firewall admits the return traffic. Treat those as separate acceptance criteria.
This example uses one static DNS backend and two listeners. It makes the protocol boundary visible without relying on public DNS, provisioning a cloud load balancer, or manufacturing benchmark results.
Allocate listeners as scarce infrastructure
TCPRoute and UDPRoute select traffic through an attached listener's protocol and port. They do not offer HTTP host/path matching. A raw TCP listener cannot distinguish tenants by HTTP Host; use the appropriate application-layer route, or TLSRoute where SNI is the routing requirement. TCPRoute's design defines this deliberately narrow model.
Consequently, granting permission to create routes is insufficient as a tenancy policy. Assign a listener to an owner, restrict its allowed route kinds and namespaces, and prevent competing claims through review or admission policy. Multiple routes targeting one raw listener can be Accepted while precedence selects the oldest route, then namespace/name ordering for ties. An Accepted condition alone does not prove that your intended backend receives traffic.
Keep attachment permission and backend permission distinct. Gateway allowedRoutes governs who can attach; ReferenceGrant authorizes cross-namespace backend references. This example keeps both in one namespace. Production platforms should model these permissions separately rather than making every namespace eligible for every listener.
Image. Two protocol branches with separate attachment gates converge on one DNS backend
The UDPRoute design leaves shared numeric TCP/UDP listener ports optional and weighted UDP backends at Extended support. Separate ports and one backend avoid those portability assumptions. Standard API availability is a baseline, not a promise about every advanced feature.
Install a matched API and controller
Use an empty, disposable local Kubernetes cluster, Kubernetes 1.33–1.36, Helm 3, a matching kubectl, and a client with BIND dig. Commands use Bash. Envoy's compatibility matrix pairs Envoy Gateway 1.9 with Gateway API 1.6.1. Pin the chart to 1.9.2. The CoreDNS image below is pinned to 1.14.7; its release is the backend version reference.
Code example. Refer to the written article for the complete code.
The chart installs CRDs on this empty cluster. Inspect their served versions instead of applying an older tutorial unchanged. The pinned 1.6.1 Standard UDPRoute CRD serves v1 and does not serve v1alpha2. Envoy's 1.9 release notes also warn that older Gateway API CRDs can cause TCP/UDP routes to be skipped. Updating a controller and updating installed CRDs are separate operations, particularly on an existing cluster.
A complete DNS example with explicit transport boundaries
Save this as l4-demo.yaml. The EnvoyProxy extension selects NodePort, avoiding automatic cloud load-balancer provisioning. externalTrafficPolicy: Cluster allows requests arriving at a node without a local Envoy endpoint; it does not guarantee preservation of the original client address. The Gateway references this namespaced infrastructure configuration.
Code example. Refer to the written article for the complete code.
CoreDNS's hosts plugin supplies a static answer; there is no upstream forwarding. Route backendRefs.port identifies the Service port, while the Gateway listener and allocated NodePort are different port boundaries. Both Service entries explicitly declare their transport.
Code example. Refer to the written article for the complete code.
Before probing, inspect Gateway/listener conditions and each route parent's Accepted and ResolvedRefs conditions. Check observedGeneration against the current resource generation. Also inspect the generated Service: a reconciled Gateway is not proof that a reachable UDP socket exists at your chosen client address.
Test UDP without accidentally testing TCP
Set the following variables to a reachable local node address and the discovered NodePorts. The examples are commands to run, not reported results. A successful answer should contain 192.0.2.10 for both queries.
Code example. Refer to the written article for the complete code.
+notcp +ignore prevents a truncated UDP reply from being retried over TCP. A TCP-only check can conceal a broken UDP path. kubectl port-forward supports TCP only; it cannot validate UDPRoute.
Containerized cluster nodes may be unreachable from the host, especially across Windows/Docker networking. Use a client inside the cluster or explicit TCP and UDP port mappings in that case. An in-cluster check validates proxy routing but does not validate external firewalls or node reachability. Do not interpret it as end-to-end production evidence.
Recover a route attached to the wrong listener
Consider an illustrative configuration error: UDPRoute dns-udp references sectionName: dns-tcp. The object can be stored successfully while attachment fails. Inspect its parent conditions and the named listener's supported kinds before changing firewall rules. Correct the reference, wait for current-generation conditions, and repeat the UDP query.
Code example. Refer to the written article for the complete code.
If attachment is healthy but UDP still times out, follow the actual packet boundaries: generated Service UDP port, node exposure, network policy, ready backend endpoints, and return traffic. A healthy TCP answer narrows the investigation; it does not eliminate protocol-specific filtering.
Envoy's TCP/UDP proxying is nontransparent. Backend access controls and per-client rate limits that relied on the original source address require redesign. An IP allowlist admitting Envoy is a different trust boundary from one admitting individual clients.
Define ownership before production rollout
The platform team owns listener allocation, exposure, controller/CRD compatibility and transport observability. Service owners own backend protocol, readiness and application behavior. Joint acceptance should cover intended-backend selection, UDP-only probes, client-identity consequences and a rollback route to the existing endpoint.
Remove this isolated example when finished:
Code example. Refer to the written article for the complete code.
These commands assume everything was created exclusively for this exercise. Retain shared CRDs and shared controllers on shared clusters. Neither a GA resource nor a green reconciliation condition replaces a verified traffic contract.
Frequently asked questions
Can TCPRoute route by HTTP Host or path? No. Its raw listener model uses protocol and port; use an application-aware route for HTTP matching.
Can I validate UDPRoute with kubectl port-forward? No. Port-forward supports TCP; send a real UDP query through a UDP-capable path.
Does Accepted guarantee the intended backend receives traffic? No. Check current-generation conditions, competing route precedence, generated exposure and an actual protocol-specific application response.