Repository navigation
Description
Summary
On Cozystack v1.5.2 with the Gateway API publishing path enabled (tenant.spec.gateway: true + platform gateway.enabled: true), the LB-IPAM assignment on the generated cilium-gateway-* Service flaps continuously: status.loadBalancer.ingress alternates between the assigned IP and empty every 1–2 seconds, indefinitely. Any consumer of the Service status is broken by this — in our case the Cilium BGP control plane (v2 CRDs) advertises and withdraws the /32 route ~2 times per second (our upstream FRR router logged 520 BGP UPDATE messages in under 4 minutes for a single prefix), making BGP-based LoadBalancer reachability unusable.
This looks like the "known-bad interaction between LB-IPAM and Gateway" mentioned as out-of-scope in #2586 — opening this issue since we could not find a dedicated tracking issue, and we have a fairly complete reproduction + diagnosis.
Environment
- Cozystack v1.5.2 (variant
isp-full), installed viacozy-installerHelm chart - Talos v1.13.0 (Cozystack nocloud image), Kubernetes v1.34.3
- 3 combined control-plane+worker nodes (Proxmox VE, 8 vCPU / 32 GB each, 2 NICs: mgmt + external)
- Cilium: Cozystack build
cilium:v1.5.2(Cilium 1.19.3), operatorquay.io/cilium/operator-generic:v1.19.3(also reproduced with operator v1.19.5) - Networking: default
kubeovn-ciliumvariant (Kube-OVN primary CNI + Cilium chained, kube-proxy replacement,routing-mode=native)
Configuration
Tenantroot:gateway: true→TenantGateway→Gatewaytenant-root/cozystack(classcilium),certMode: existingSecret(wildcard secret)- Platform package:
gateway.enabled: true,attachedNamespacesper docs → dashboard published viaHTTPRoute(works) CiliumLoadBalancerIPPoolwith a single/32block; the generated Servicetenant-root/cilium-gateway-cozystackgets that IP- Cilium BGP control plane enabled with
CiliumBGPClusterConfig/CiliumBGPPeerConfig/CiliumBGPAdvertisement(advertisementType: Service,addresses: [LoadBalancerIP]), eBGP peering with an FRR router — sessions establish fine from all 3 nodes externalTrafficPolicyof the gateway Service isCluster(so not Gateway API and BGP control plane cilium/cilium#36804)
Observed behaviour
- Sampling the Service once per second shows the LB IP flapping (empty ↔ assigned, several transitions per 20 s window):
[1] ip=172.16.255.253
[2] ip=
[3] ip=172.16.255.253
[4] ip=
[5] ip=172.16.255.253
...
cilium-operatorlogs show a tight reconcile loop (~1/s, continuous, no errors):
level=info msg="Reconciling Gateway" ... controller=gateway resource=tenant-root/cozystack
level=info msg="Successfully reconciled Gateway" ...
level=info msg="Reconciling GAMMA service" ... controller=gamma resource=tenant-root/cilium-gateway-cozystack
(repeats forever, ~40 "Reconciling Gateway" per minute)
-
managedFieldscaptured at the moment the status is empty showcilium-operator-lb-ipamis the only writer ofstatus(owning an emptyf:loadBalancer.f:ingress), i.e. LB-IPAM itself releases and re-assigns the IP on every Service update performed by the gateway/GAMMA reconcilers. -
The BGP speaker faithfully follows the Service status → advertise/withdraw storm. FRR peer stats:
Updates Rcvd: 520within a ~4 min session for one prefix. External reachability alternates between working /connection refused/no route to host. -
Notably, the datapath is unaffected: the BPF service entry for the VIP stays programmed the whole time; with a static route to a node IP, HTTPS requests to the VIP return 302/200 consistently while the status keeps flapping.
What we ruled out / tried
externalTrafficPolicyisCluster(not the Gateway API and BGP control plane cilium/cilium#36804 case)- Restarting
ciliumagents: no change (datapath resync ok, flap continues) - Restarting
cilium-operator: no change - Pinning the IP with
lbipam.cilium.io/ipson the Service: annotation persists, flap continues - Adding
spec.externalIPsto advertise viaExternalIPinstead: the gateway controller's periodic re-apply removes the field within ~40 s (SSA prune), so no stable workaround on the Service object itself - Bumping only the operator to
quay.io/cilium/operator-generic:v1.19.5: reconcile loop and flap unchanged - k8s client rate limit raised (
k8sClientRateLimit: qps 100 / burst 200) and L2-announcement lease timers relaxed earlier in the investigation: unrelated to this loop
Expected behaviour
The cilium-gateway-* Service should keep its LB-IPAM address stably; gateway/GAMMA reconciliation should not cause LB-IPAM to release/re-assign the IP, so that BGP service advertisement (and anything else reading status.loadBalancer) is stable.
Questions
- Is there a dedicated upstream/cozystack tracking issue for this interaction (referenced in Migrate LoadBalancer announcer from MetalLB to Cilium native (LB-IPAM + L2) #2586)? Happy to close this as duplicate if so.
- Is a fixed Cilium version already identified for the Cozystack v1.6 line?
Workaround we settled on
Static route for the VIP /32 towards a node IP on the external network (datapath is stable), keeping the established BGP sessions in place for when the fix lands.
Activity
lfinmauritius commentedon Jul 15, 2026
Follow-up with additional findings that narrow the bug down to LB-IPAM itself, independent of the Gateway API controllers:
The flap reproduces with Gateway API fully disabled
After rolling back platform publishing to ingress-nginx (
gateway.enabled: false, tenantgateway: false, TenantGateway/Gateway deleted) and disabling the Cilium Gateway API controllers entirely (gatewayAPI.enabled: false→enable-gateway-apiremoved from cilium-config, gateway/GAMMA reconcilers no longer running, confirmed 0 reconcile log lines), the same release/re-assign cycle continued on the ingress-nginx LoadBalancer Service (tenant-root/root-ingress-controller):status.loadBalancer.ingressstill alternated between the assigned IP and empty every 1–2 s at full cluster quiescence (measured 7 transitions over a 30 s window, no HR reconciliations in progress, no Service spec changes).So the gateway/GAMMA reconcile churn amplifies the issue (more Service update events → more cycles), but the release/re-assign behaviour is in LB-IPAM's own reconcile path on this build (Cozystack
cilium:v1.5.2, Cilium 1.19.3). Also reproduced withquay.io/cilium/operator-generic:v1.19.5(operator-only bump): no change.Side-effect worth noting: hours of this churn progressively corrupted the datapath — we captured return packets leaving with pod IPs instead of the reverse-NATed VIP (BPF conntrack inconsistencies), fixed transiently by restarting the cilium DaemonSet, but degrading again while the churn continued.
Workaround that gives full stability on v1.5.2
Remove LB-IPAM from the picture entirely:
kubectl patch svc root-ingress-controller -n tenant-root --type merge -p '{"spec":{"externalIPs":["<VIP>"],"externalTrafficPolicy":"Cluster"}}'CiliumLoadBalancerIPPool(no pool → no allocation → nothing left to flap).CiliumBGPAdvertisementwithservice.addresses: [LoadBalancerIP, ExternalIP]→ the ExternalIP is advertised continuously.Result: BGP announcement stable (5/5 over 30 s vs 6/10 before), datapath steady at ~9 ms, no pod-IP leaks after a final agent restart. Note this pinning is only viable on the nginx Service — on the Gateway-generated Service the controller's periodic server-side apply prunes foreign fields (
spec.externalIPswas removed within ~40 s), which is also what feeds LB-IPAM with a constant stream of update events.Happy to test a patched build — the environment reproduces the issue within minutes.