Scenario 07 · Single Load Balancer
Azure's Standard Load Balancer allows roughly 300 inbound rules per node NIC, and
AKS puts every Service type=LoadBalancer on one Load Balancer — where each
Service port costs about one rule. Gateway API implementations that provision a dedicated proxy
and a LoadBalancer Service per Gateway burn through that budget fast: a Gateway per
team or tenant, and a few hundred of them later, new ingress simply stops provisioning. The fix
isn't a bigger limit — it's not minting an L4 frontend for every route in the first place.
Architecture
Hostnames, SNI and paths are matched inside the proxy — above the Layer-4 Load Balancer — so adding tenants, Gateways or routes never touches the Azure rule budget. The L4 frontend count stays at one.
The setup
The 300-rule limit is a Layer-4 limit: it grows with the number of L4 frontends you
create, not with HTTP routes or hostnames. So you expose the data plane once — a single
LoadBalancer Service on standard ports — and let it carry every Gateway:
dataPlane:
service:
type: LoadBalancer # the ONLY L4 frontend in the cluster
ports:
- { name: http, port: 80 }
- { name: https, port: 443 } # 2 ports ≈ 2 Azure rules, flat
replicas: 3 # scale the proxy, not the LB count
Every team then ships its own Gateway as usual. Because they all bind the same shared
gatewayClassName, ZenoIngress folds them into that one data plane — no new
LoadBalancer Service, and +0 Azure rules per Gateway:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata: { name: team-a, namespace: team-a }
spec:
gatewayClassName: zenoingress # shared data plane — no new LB Service
listeners:
- name: https
hostname: "a.example.com"
port: 443
protocol: HTTPS
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata: { name: team-b, namespace: team-b }
spec:
gatewayClassName: zenoingress # same data plane, same LB, +0 Azure rules
listeners:
- name: https
hostname: "b.example.com"
port: 443
protocol: HTTPS
Add Gateway C, D … N and a thousand HTTPRoutes behind them — the cluster still owns exactly
one LoadBalancer Service.
What happens
Service type=LoadBalancer on one Standard
Load Balancer, and each Service port consumes roughly one inbound rule on the node
NIC — capped at 300 per NIC.
LoadBalancer
Service for every Gateway. Tenants × listeners × ports each take a slice of the budget.
budget burns
:80 and :443 — two ports, so about
two Azure rules, and the count stays flat as Gateways and routes grow.
~2 rules
Outcome
One shared data plane behind a single LoadBalancer Service means tenants, Gateways and routes scale on HTTP routing — not on Azure's Layer-4 rule budget. The L4 frontend count is a constant, not a function of how many teams you onboard.
The honest caveats
LoadBalancer Service per Gateway — and any product,
ZenoIngress included, would then consume Azure rules per Gateway.
Run this yourself
List every LoadBalancer Service and its ports. The total of
Services × ports is roughly your Azure inbound-rule count today — under the shared
model that list collapses to a single entry:
$ # per-Gateway cluster: one LB Service per Gateway
$ kubectl get svc -A --field-selector spec.type=LoadBalancer \
-o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,PORTS:.spec.ports[*].port
NS NAME PORTS
team-a gw-team-a-lb 443
team-b gw-team-b-lb 443
team-c gw-team-c-lb 443,8443
… (one per Gateway) …
# 240 Services × ports ≈ 240 Azure rules — closing in on 300
$ # same command, under ZenoIngress' shared data plane
$ kubectl get svc -A --field-selector spec.type=LoadBalancer \
-o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,PORTS:.spec.ports[*].port
NS NAME PORTS
zeno-system zenoingress-data 80,443
# 1 Service × 2 ports ≈ 2 Azure rules — flat as Gateways grow
Sources: Microsoft Learn — Azure Load Balancer limits, Standard Load Balancer in AKS, Multiple Standard Load Balancers in AKS.
A single shared data plane for the whole cluster. Free for one production cluster.
Deploy Free