Skip to main content

Gateway API v1.6 Graduates UDPRoute to GA: What It Actually Takes to Route Non-HTTP Traffic on a Self-Hosted PaaS

9 min readDora NodaDora Noda
Share
On this page

Gateway API v1.6.0 shipped on June 29, 2026, and the headline is simple: UDPRoute is General Availability, promoted out of the v1alpha2 experimental channel alongside TCPRoute and TLSRoute. For a self-hosted PaaS, that reads like good news — DNS, QUIC, game servers, and custom protocols finally get a stable, typed CRD instead of an experimental one that could change shape under you. But "GA in the spec" and "works on your cluster today" are two different claims, and the gap between them is where this actually gets interesting.

Here's the short version, before the detail: whether UDPRoute helps you this week depends entirely on which Gateway API implementation you run, there's a real upgrade-order trap that will drop traffic if you get it wrong, and even once it works, UDPRoute doesn't solve multi-tenant routing the way HTTPRoute does — it creates a new capacity-planning problem instead. We'll walk through all three with a working example.

What Actually Graduated in v1.6

Gateway API versions its resources independently — a CRD can be GA while a sibling CRD in the same API group is still experimental. As of v1.6.0, the following are GA under the gateway.networking.k8s.io/v1 API group:

ResourceStatus as of v1.6.0
GatewayClass, Gateway, HTTPRoute, GRPCRouteGA (unchanged, already stable)
TLSRouteGA
TCPRouteGA (new in v1.6)
UDPRouteGA (new in v1.6)
ListenerSet, BackendTLSPolicy, ReferenceGrantGA

The practical change for TCPRoute and UDPRoute is that their stored apiVersion moves from gateway.networking.k8s.io/v1alpha2 to gateway.networking.k8s.io/v1, and the v1alpha2 versions of both are now marked for future removal. The release also lands GEP-2645: a formal conformance test suite for UDPRoute, plus a new GATEWAY-UDP conformance profile. That last part matters more than it sounds — before v1.6, "UDPRoute support" was whatever each controller vendor decided to implement against a moving experimental spec. Now there's a pass/fail suite every implementation is measured against the same way, which is the actual substance behind "GA": not just a version bump, but a shared, testable definition of correct behavior.

None of that changes what UDPRoute does. It routes raw UDP traffic — DNS, QUIC, game server protocols, VoIP, custom binary protocols — through the same GatewayClass/Gateway resource model that's probably already handling your fleet's HTTP ingress. What it changes is whether you can build on it without the ground shifting under you.

The Controller Reality Check

Gateway API is a spec; whether UDPRoute does anything depends on the controller behind your GatewayClass. As of mid-2026, support is uneven enough that "is UDPRoute GA" is the wrong question — the right one is "does my Gateway implementation ship it, and on what version":

ImplementationUDPRoute statusThe catch
Envoy Gateway (v1.7)Full support, reconciles via gateway.networking.k8s.io/v1Must upgrade Gateway API CRDs to v1.6 and migrate existing route manifests to apiVersion: v1 before upgrading the controller — see below
CiliumNot yet reconciled into the dataplaneTCPRoute/UDPRoute support is an open feature request (tracked in a public GitHub issue) as of April 2026; the CRDs can exist in-cluster without the dataplane acting on them
TraefikSupports TCPRoute/UDPRoute from its experimental channelTraefik's own Gateway API spec support is pinned to v1.5.1 — a minor version behind v1.6, so don't assume parity with the latest conformance profile

The Envoy Gateway case is the one that will actually bite a fleet that's already on Gateway API (which, per this blog's own migration coverage, is where a PaaS routing tenant HTTPRoutes off Ingress-NGINX should already be). Envoy Gateway v1.7 requires the Gateway API CRDs to be at v1.6 first. If you have any existing TCPRoute or UDPRoute objects — even ones nobody's touched since they were created against the experimental channel — you have to convert their apiVersion to v1 before you bump the CRDs. Skip that step, or do it out of order, and those routes stop being served. Not a warning in the logs: dropped traffic, silently, on whatever was behind that route. The fix is procedural, not technical — audit existing TCPRoute/UDPRoute manifests for their apiVersion field, bump those first, then upgrade CRDs, then upgrade the controller — but it's exactly the kind of order-dependent step that's invisible until you're mid-upgrade with a page open.

A Worked Example: Adding UDPRoute Next to Existing HTTPRoutes

Say your fleet already migrated tenant ingress from Ingress-NGINX to Envoy-Gateway-backed HTTPRoute — the same Gateway object is terminating TLS on :443 for every tenant's HTTP traffic. A tenant now wants to run a stateless UDP workload: a CoreDNS instance answering on port 53, or a game server speaking a custom UDP protocol on a high port. Here's what gets added, not replaced.

First, the Gateway needs a UDP listener alongside its existing HTTPS one:

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: tenant-gateway
  namespace: gateway-infra
spec:
  gatewayClassName: envoy-gateway
  listeners:
    - name: https
      protocol: HTTPS
      port: 443
      tls:
        mode: Terminate
        certificateRefs: [{ name: tenant-wildcard-tls }]
    - name: dns-udp
      protocol: UDP
      port: 5300
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels: { tenant: acme-corp }

Then the UDPRoute itself, in the tenant's namespace, pointing at that listener by name:

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: UDPRoute
metadata:
  name: acme-coredns
  namespace: tenant-acme-corp
  labels: { tenant: acme-corp }
spec:
  parentRefs:
    - name: tenant-gateway
      namespace: gateway-infra
      sectionName: dns-udp
  rules:
    - backendRefs:
        - name: acme-coredns-svc
          port: 53

That's the entire mechanism: no annotations, no separate Service of type LoadBalancer per tenant, no hand-rolled iptables/NodePort plumbing. Traffic hitting the Gateway's node on port 5300 gets forwarded to acme-coredns-svc:53 in the tenant's namespace, governed by the same ReferenceGrant-based cross-namespace access control already protecting HTTPRoute. A game server backend works identically — swap the listener port and backendRef for whatever port the game protocol uses. This is the "custom protocol" case the v1.6 release is aimed at: anything that speaks raw UDP now gets first-class routing instead of a bespoke workaround.

The Catch UDPRoute Doesn't Solve: One Port Per Tenant

Here's the part that's easy to miss if you're used to how HTTPRoute scales on a multi-tenant Gateway. HTTPRoute and TLSRoute both have a hostnames field — HTTPRoute matches on the Host header, TLSRoute matches on the SNI value from the TLS handshake. That's what lets dozens of tenants share one :443 listener on one IP: the Gateway reads a hostname out of the request itself and routes accordingly, before any backend sees the traffic.

UDPRoute has no hostnames field. Raw UDP has no equivalent of a Host header or SNI extension for the Gateway to inspect before delivery — a UDP packet is just a payload with no routing-relevant metadata the spec defines. Matching is entirely by which listener — meaning which port — the traffic arrived on. That's why the example above needed a dedicated dns-udp listener on port 5300 rather than reusing :443. If a second tenant wants their own UDP DNS instance, they need their own port (or their own Gateway IP), full stop. There's no way to multiplex two tenants' UDP services behind one shared port the way HTTPRoute multiplexes tenants behind one shared :443.

This is worth naming explicitly for QUIC, since it's the workload most people assume "UDP routing" was built for. HTTP/3 and QUIC-based protocols run over UDP, and it's tempting to read UDPRoute GA as "now I can Gateway-route QUIC traffic the way I route HTTPS." You can carry QUIC packets through a UDPRoute listener, but you get none of TLSRoute's SNI-based multiplexing on the way — QUIC's server-name information lives inside the encrypted handshake, and UDPRoute's matching stops at "which port did this arrive on." Routing multiple tenants' HTTP/3 backends by hostname still means terminating at the HTTPRoute/HTTPS layer (where a QUIC-aware implementation exposes it) rather than passing raw UDP through — UDPRoute alone gets you a transport for QUIC traffic, not tenant-aware QUIC routing.

For a Cluster-API-managed fleet, that turns "add UDP support" into a capacity-planning problem, not just a routing one: every tenant UDP service is now a line item against a finite pool of ports (or a finite pool of Gateway-fronting IPs, if you'd rather hand out IPs than manage a port registry). A platform that's been used to HTTPRoute's near-unlimited tenant density on a single IP needs an actual allocation scheme — a reserved port range per tenant, or per-tenant Gateways with their own IP — before UDPRoute-based services go from "the one pilot customer who asked for it" to "a general-availability feature every tenant can self-serve."

What to Actually Do With This

If your fleet is already on Gateway API for HTTP ingress — which, if you followed this blog's Ingress-NGINX retirement coverage, it should be by now — UDPRoute GA is the reason to finish standardizing on the full route-type family rather than bolting on a one-off NodePort hack the next time a tenant asks for a UDP-based service. The concrete steps, in order:

  1. Confirm your GatewayClass implementation actually ships UDPRoute dataplane support, not just the CRD. Check the version, not just the vendor name — Cilium and older Traefik builds will accept a UDPRoute object without doing anything with it.
  2. Audit for any existing TCPRoute/UDPRoute manifests before touching CRD versions. If they're still v1alpha2, convert them to v1 first — do this before the CRD upgrade, not after, or those routes go dark.
  3. Design a port/IP allocation scheme before the first UDP tenant, not after the third one. UDPRoute's lack of hostname-based matching means this is a capacity decision, not a routing detail.

Getting the route-type family standardized now — HTTPRoute, TLSRoute, TCPRoute, and UDPRoute all on the same GatewayClass — is exactly the lesson this blog's own Ingress-NGINX retirement post already drew from a different angle: doing a disciplined migration once, on your own schedule, beats getting forced into a second one later because half your fleet's traffic never had a real routing story to begin with.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, all routed through the same Gateway API stack this post describes. Star the repo on GitHub or deploy your first app today.

Sources

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex