Cilium LoadBalancer IPAM and BGP Service Advertisement
In the previous post, we established BGP peering between our Kubernetes cluster and a virtual leaf-spine datacenter fabric, enabling dynamic Pod CIDR advertisement.
But Pod connectivity is only half the story.
In production Kubernetes environments, Services β especially LoadBalancer type services β are how applications expose themselves to the outside world. Traditionally, this requires external load balancers like MetalLB or cloud provider integrations.
With Cilium’s LoadBalancer IPAM and BGP Service Advertisement, we can eliminate that dependency entirely.
In this post, we’ll extend our lab to:
- Allocate LoadBalancer IPs from custom IP pools
- Advertise Service IPs via BGP to the datacenter fabric
- Implement selective advertisement using label-based filtering
- Verify end-to-end reachability from the core router to Kubernetes Services
Why LoadBalancer IPAM Matters
In bare-metal or on-premises Kubernetes deployments, creating a Service of type LoadBalancer typically leaves it stuck in <pending> state β because there’s no cloud provider to assign an external IP.
Cilium solves this with LB-IPAM, which:
- Assigns IPs from administrator-defined pools
- Integrates seamlessly with Cilium’s BGP control plane
- Supports multi-tenant IP allocation using selectors
- Advertises allocated IPs directly into your network fabric
No external controllers. No cloud dependencies. Just native Kubernetes networking.
The Architecture
Our setup builds on the existing BGP topology:
- Kubernetes nodes already peer with ToR switches
- ToRs peer with the spine (core router)
- New: LoadBalancer IPs are allocated from a dedicated pool and advertised via BGP
When a Service of type LoadBalancer is created:
- Cilium’s LB-IPAM assigns an IP from the pool
- The BGP control plane advertises this IP to ToR switches
- ToRs propagate the route to the spine
- Traffic can now reach the Service from anywhere in the fabric
Defining an IP Pool
We start by creating a CiliumLoadBalancerIPPool resource that defines which IPs can be allocated for Services.
pool.yaml
This pool:
- Allocates IPs in the range
20.0.20.100β20.0.20.200 - Only applies to Services labeled with
color: yellow|red|blue
The serviceSelector gives us fine-grained control over which Services can consume IPs from this pool β critical for multi-tenant environments.
Apply it:
Configuring BGP Service Advertisement
Next, we configure which Services should be advertised via BGP.
We create a CiliumBGPAdvertisement resource with:
- Advertisement type:
Service - Address type:
LoadBalancerIP - Label-based filtering for selective advertisement
cilium-bgp-advertisement.yaml
This policy states:
Advertise LoadBalancer IPs only for Services that are:
- Labeled
color: blueorcolor: red- In namespace
tenant-aortenant-b
This is powerful. We can:
- Isolate tenant traffic
- Prevent unwanted services from leaking into the fabric
- Implement namespace-based routing policies
Updating BGP Peer Configuration
We need to ensure our existing CiliumBGPPeerConfig includes the new services advertisement.
The updated configuration references both the pod-cidr and services advertisements:
cilium-bgp-peering-policies.yaml (updated)
Apply the updated policies:
Now Cilium will advertise both:
- Pod CIDRs (from all nodes)
- LoadBalancer Service IPs (from matching services)
Creating Test Services
Let’s create two Services to test the configuration.
Service Blue (tenant-a)
service-blue.yaml
Service Red (tenant-b)
service-red.yaml
Create the namespaces and services:
Check the allocated IPs:
Both services immediately receive IPs from the pool-primary range β
Verifying BGP Advertisement
Now for the moment of truth β are these Service IPs actually advertised into the fabric?
Let’s check the BGP table on tor0:
Perfect! π―
We can see:
20.0.20.100/32β advertised by bothkind-control-planeandkind-worker(ECMP)20.0.20.101/32β advertised by bothkind-control-planeandkind-worker(ECMP)
The ToR has learned these Service IPs via iBGP from the Kubernetes nodes in rack0.
And notice the = symbol β these routes have equal-cost multipath (ECMP) enabled, meaning traffic can be load-balanced across multiple nodes.
How ECMP Works for Services
Because both nodes in rack0 advertise the same Service IP, the ToR switch sees two equal-cost paths to reach 20.0.20.100.
This is exactly what we want:
- Traffic destined for the Service can enter via any node
- Cilium’s eBPF dataplane will forward packets to the correct backend Pod
- If one node fails, traffic automatically uses the other path
This is native, fabric-level load balancing β no external load balancer required.
Testing End-to-End Connectivity
To verify everything works, let’s test connectivity from the core router (spine) to a Service IP.
From router0:
Success! β
The core router can reach the Service IP, meaning:
- BGP advertisement is working
- Routing is correct across the fabric
- Cilium is properly handling Service traffic
What About Services in Other Namespaces?
What if we create a Service that doesn’t match the advertisement policy?
Let’s try:
This Service:
- Has label
color: green(notblueorred) - Is in namespace
tenant-c(nottenant-aortenant-b)
After applying:
It receives an IP from the pool (because the pool selector matches color: green), but if we check the BGP table:
The IP is not advertised β
This confirms our policy is working:
- LB-IPAM allocated the IP
- BGP advertisement was blocked by the selector
This separation is key for multi-tenant security.
Key Takeaways
With Cilium’s LoadBalancer IPAM and BGP Service Advertisement, we’ve achieved:
- No external load balancers required β Cilium handles IP allocation natively
- BGP-driven Service reachability β Services are routable from anywhere in the DC fabric
- Label-based policy control β Fine-grained control over which Services are advertised
- ECMP load balancing β Multiple nodes can announce the same Service IP for redundancy
- Multi-tenancy support β Namespace and label selectors enable secure isolation
This is how LoadBalancer Services should work in modern on-premises Kubernetes.
Real-World Use Cases
This architecture is perfect for:
- Bare-metal Kubernetes β No cloud provider? No problem.
- On-prem data centers β Integrate Kubernetes directly into existing BGP fabrics
- Multi-tenant platforms β Use labels to control Service advertisement per team/namespace
- Hybrid cloud β Run consistent networking across cloud and on-prem
What’s Next?
In future posts, we could explore:
- IPv6 Service advertisement
- Anycast Services using BGP
- BGP communities for traffic engineering
- Integration with external DNS for automatic Service discovery
Final Thoughts
Cilium continues to prove itself as the most powerful CNI for production Kubernetes deployments.
The combination of:
- Native routing
- BGP control plane
- LoadBalancer IPAM
- eBPF dataplane
β¦makes Cilium the gold standard for on-premises and bare-metal Kubernetes networking.
If you’re running Kubernetes outside the cloud, Cilium isn’t just an option β it’s the right choice.
Happy routing π