Two deadlines collided in the first half of 2026, and together they redraw how a self-hosted platform routes tenant traffic. On March 31, 2026, the Kubernetes project retired Ingress-NGINX — the controller behind roughly half of all cloud-native ingress deployments stopped receiving security patches. A month earlier, on February 27, Gateway API v1.5.0 shipped with Gateway merging — the ListenerSet resource, GEP-1713 — graduated to the Standard channel.
Here is the answer those two events add up to, stated up front: a self-hosted platform no longer needs one load balancer, one external IP, or even one Gateway per tenant. One platform-owned Gateway, backed by one load balancer, can now carry every tenant's HTTPS listeners — with each tenant defining its own hostnames and managing its own TLS certificates in its own namespace, and the Gateway controller merging them all onto shared infrastructure. On a Hetzner-based fleet, that turns twenty load balancers at €7.49/month each (about €150/month from April 2026 pricing) into one, and it replaces the per-tenant Ingress sprawl you were about to migrate off anyway. The catch, which this post takes just as seriously: the shared listener set becomes something your platform's control plane must reconcile safely across tenants, because the isolation boundary that used to be "separate Gateway, separate blast radius" is gone.
Let's walk through what actually went stable, the before/after topology, a working example, the load balancer math, and the reconciliation work the merged model shifts onto you.
What Actually Went Stable in v1.5
Gateway API v1.5.0 was tagged on February 27, 2026, and the project detailed the release in an April 2026 announcement. It is the first release under the project's new release-train model, and its headline is graduation: TLSRoute, HTTPRoute CORS filtering, client certificate validation, and — most consequential for multi-tenant platforms — ListenerSet, the standard mechanism to merge multiple listener definitions onto a single Gateway.
The mechanics matter, so here they are concretely:
- A ListenerSet is a separate, namespaced resource that defines listeners — hostname, port, protocol, TLS config — and attaches them to a target Gateway. The Gateway controller merges listeners from the Gateway itself plus every attached ListenerSet into one effective listener list on the same underlying data plane.
- Merging is opt-in and gated by the Gateway owner. A Gateway must explicitly allow attachment via
spec.allowedListeners.namespaces, with four modes:None(the default — nothing attaches),Same(only the Gateway's own namespace),Selector(namespaces matching a label selector), andAll. - Precedence is deterministic. The parent Gateway's own listeners rank highest; ListenerSets follow, ordered by creation timestamp (oldest first), then alphabetically by
{namespace}/{name}. When two listeners claim the same hostname and port, the higher-precedence one wins and the loser gets aConflicted: Truecondition. - The 64-listener ceiling is gone. A single Gateway resource maxes out at 64 listeners, which in practice capped large deployments at around a thousand domains and made the Gateway, in GEP-1713's own words, "a point of contention since it is the only place to attach listeners with certificates." ListenerSets shard that list across many resources while keeping one data plane.
- Status is per-listener and per-ListenerSet. Each ListenerSet reports
Accepted,Programmed, andConflictedconditions, and the Gateway's status counts attached ListenerSets — so a tenant can see their own listener's health without reading the platform's Gateway.
That last point is the quiet design win: ListenerSet is not just a scalability fix, it's a delegation primitive. The platform team owns the Gateway; application teams own their listeners.
The Before Picture: Why Multi-Tenant Ingress Sprawled
To see what merging changes, look at what a self-hosted PaaS routing per-tenant subdomains and custom domains had to build before it.
Under the Ingress model, tenants got self-service: each app shipped an Ingress object with its own hostnames and a TLS secret in its own namespace, and cert-manager issued certificates per Ingress. That self-service is exactly what teams migrating to Gateway API complained about losing — under pre-1.5 Gateway API, every listener and every certificate reference had to live on the one central Gateway object, which only the platform team could write. Your choices were bad in both directions:
- One Gateway per tenant. Clean isolation, tenant-owned certs — but every Gateway of type LoadBalancer provisions its own cloud load balancer and external IP. Twenty tenants, twenty LBs, twenty IPs, twenty monthly line items.
- One shared Gateway, platform-owned listeners. One LB — but every tenant custom-domain onboarding becomes a ticket against the central Gateway, the 64-listener cap looms, and the Gateway object becomes the contention point GEP-1713 describes: projects like Knative generating thousands of per-service certificates hit O(1000) distinct listeners with nowhere to put them.
Here's the topology shift in one table, for a platform with N tenant apps:
| One Ingress/Gateway per tenant | One Gateway + N ListenerSets (v1.5) | |
|---|---|---|
| Load balancers provisioned | N | 1 |
| External IPs consumed | N | 1 |
| Who writes listener + TLS config | Tenant (Ingress) / Platform (Gateway) | Tenant, in their namespace |
| Listener ceiling | 64 per Gateway | Effectively unbounded |
| Isolation boundary | The Gateway/LB itself | Merge semantics + admission policy |
| DNS for custom domains | Per-tenant LB IP | One stable IP/anycast target |
The last two rows are the honest trade: you give up infrastructure-level isolation and take on merge-level reconciliation. We'll price both sides.
A Worked Example: Platform Gateway, Tenant ListenerSet
Here is the shape of the merged model on a self-hosted cluster running Envoy Gateway (currently the most complete conformant implementation — see the reality check below). The platform team owns one Gateway in platform-system and admits ListenerSets only from namespaces labeled as tenant namespaces:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: fleet-gateway
namespace: platform-system
spec:
gatewayClassName: envoy-gateway
listeners:
- name: default-http # Gateway must keep >= 1 listener of its own
protocol: HTTP
port: 80
hostname: "*.apps.example.com"
allowedListeners:
namespaces:
from: Selector
selector:
matchLabels:
platform.example.com/tenant: "true"A tenant onboarding the custom domain shop.acme.com writes, entirely inside their own namespace:
apiVersion: gateway.networking.k8s.io/v1
kind: ListenerSet
metadata:
name: acme-domains
namespace: tenant-acme
spec:
parentRef:
name: fleet-gateway
namespace: platform-system
kind: Gateway
group: gateway.networking.k8s.io
listeners:
- name: shop-https
protocol: HTTPS
port: 443
hostname: "shop.acme.com"
tls:
mode: Terminate
certificateRefs:
- name: shop-acme-com-tls # secret lives in tenant-acmeAnd the certificate that fills that secret, also tenant-owned:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: shop-acme-com
namespace: tenant-acme
spec:
secretName: shop-acme-com-tls
dnsNames: ["shop.acme.com"]
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuerTwo properties of this setup are worth underlining:
- Certificate isolation is structural, not conventional. A ListenerSet resolves
certificateRefsonly within its own namespace, and ReferenceGrants deliberately do not cascade across the merge: the Gateway's listeners cannot read tenant secrets, and tenant ListenerSets cannot read the platform's or each other's. Losing a tenant namespace to compromise does not expose anyone else's keys. - TLS terminates in-cluster, at the shared data plane. The cloud load balancer in front doesn't need to know about any of these certificates — which is exactly what makes the single-LB math work on Hetzner.
The Hetzner Load Balancer Math
Now the part the pricing page never shows you. Hetzner's smallest load balancer, the LB11, costs €5.39/month today and rises to €7.49/month on April 1, 2026 — a 36% increase in that spring's across-the-board repricing. An LB11 carries up to 5 services, 25 targets, 10 managed certificates, 10,000 concurrent connections, and 1 TB of included traffic.
Run both topologies through those numbers for a 20-tenant fleet:
- Per-tenant LBs: 20 × €7.49 = €149.80/month, plus 20 public IPv4 addresses, plus 20 objects for your provisioning code to create, monitor, and garbage-collect. And the LB11's 10-certificate limit means any tenant with more than a handful of custom domains forces an upgrade or a second LB anyway.
- One shared LB in TCP passthrough mode: €7.49/month. The LB forwards ports 80/443 to the Gateway's Envoy endpoints as plain TCP; TLS terminates in-cluster at the merged Gateway, so Hetzner's 10-cert limit simply never applies — 20 tenants or 200 domains cost the same at the LB layer. Two services used (80, 443), well under the 5-service cap; targets are your ingress node pool, well under 25 for a small fleet.
The sensitivity curve is blunt: the shared model saves €7.49 for every tenant past the first, so it wins at two tenants and the gap only widens — at 50 tenants you're comparing €374.50 against €7.49. What actually bounds the single-LB design isn't cost but capacity and availability: 10,000 concurrent connections and 1 TB of traffic are the LB11's real ceilings, and one LB is one failure domain. The proportionate answer for most fleets is a second LB11 for redundancy behind round-robin DNS — €14.98/month total, still a rounding error against €150 — with a bigger LB tier as the upgrade path, not more LBs per tenant.
The New Job: Reconciling a Shared Listener Set Safely
That €140/month of savings is paid for in control-plane responsibility. Under one-Gateway-per-tenant, a tenant misconfiguring TLS broke their Gateway. Under merging, every tenant's listeners share one Gateway's status, one data-plane config, one blast radius. Three specific hazards move onto your platform's plate:
Hostname conflicts become a tenant-vs-tenant dispute. If tenant-acme and tenant-mallory both claim shop.acme.com:443, the spec resolves it by precedence — the older ListenerSet wins, the newer one gets Conflicted: True. Deterministic, but note what it rewards: whoever created their resource first holds the hostname. A malicious or careless tenant who registers someone else's domain early effectively squats it at the routing layer.
The fix is admission, not merging. Precedence is a tiebreaker, not an authorization model. A platform admitting tenant ListenerSets needs a validating admission policy in front: verify domain ownership (a DNS TXT challenge, or your control plane's own custom-domain verification flow) before a ListenerSet naming that hostname is allowed into the cluster at all. This is the same job your PaaS control plane already does for custom-domain onboarding — the difference is that it's now load-bearing for cross-tenant isolation, not just correctness.
Status watching is now a platform duty. A Conflicted or non-Programmed condition on any tenant's ListenerSet is a routing outage for that tenant that they may not have permissions to diagnose fully. Your control plane should surface per-ListenerSet conditions into whatever status API tenants (or their agents) already read — the merged Gateway's health is a fleet-level signal, but the per-listener conditions are each tenant's.
None of this is harder than what a multi-tenant platform already does for namespaces, quotas, and NetworkPolicies. But it must exist before you flip allowedListeners from None to Selector — the default is closed for a reason.
Reality Check: Can Your Stack Actually Use This Yet?
"Stable in the spec" and "usable on your cluster" are different claims in mid-2026. The support matrix:
| Component | ListenerSet status |
|---|---|
| Envoy Gateway | Supported; appears in conformance reports |
| Cilium Gateway API | Optional — requires installing the ListenerSet CRD; disabled without it |
| Istio | Not yet supported; open discussion targets a future release (1.30+) |
| cert-manager | Alpha in v1.20, explicitly flagged not-for-production |
Seven implementations were fully conformant with Gateway API v1.5 shortly after release, but ListenerSet specifically is where you must check your own data plane. If your fleet runs Cilium as CNI, its Gateway controller can do this — with the CRD installed deliberately. If you're on Istio, the merged-listener model isn't available to you yet, and a shared Gateway with platform-owned listeners (plus tickets) remains the interim state. The cert-manager caveat matters too: the tenant-owned Certificate flow in the example above works today, but deeper cert-manager integration that watches ListenerSets natively (the equivalent of its Gateway annotations) is still maturing.
Versus One-Ingress-per-Tenant: The Decision, Compressed
Since Ingress-NGINX's retirement is forcing the migration anyway, the real question isn't "should we adopt Gateway API" — it's which multi-tenant shape to land on. Compressed:
| One Ingress per tenant (retiring) | One Gateway per tenant | One Gateway + ListenerSets | |
|---|---|---|---|
| Tenant cert self-service | Yes | Yes | Yes |
| LB/IP count for N tenants | Shared controller, 1–few LBs | N | 1 |
| Isolation boundary | Controller config merge (annotations) | Infrastructure | Merge semantics + admission |
| Security patches after 3/2026 | None | Yes | Yes |
| Listener scale | Controller-dependent | 64/Gateway | Unbounded |
| Platform work required | Annotation sprawl, EOL risk | LB provisioning autom. | Admission + status plumbing |
It's worth being precise about what sprawled under Ingress: usually not the controller itself (often shared) but the per-tenant annotation surface and, for teams that isolated tenants with dedicated controllers or Gateways, the LB fleet. ListenerSet is the first standard-channel primitive that delivers all three at once — tenant self-service, one load balancer, and a supported API — at the cost of admission and status work your platform was arguably supposed to own anyway.
For a self-hosted PaaS on owned hardware, that trade lands clearly on the merged side: the marginal cost of a tenant's custom domain drops to zero at the infrastructure layer, the per-tenant LB line item disappears, and the reconciliation work concentrates in the control plane you already operate — which is where a platform's intelligence belongs.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, with per-app subdomains and custom domains routed through a platform-owned gateway rather than a per-app load balancer bill. Star the repo on GitHub or deploy your first app today.



