---
title: "Advertising Kubernetes Service IPs with Calico and BGP"
source: "https://www.tigera.io/blog/advertising-kubernetes-service-ips-with-calico-and-bgp/"
---

[Technical Blog](https://www.tigera.io/category/technical-blog/)

# Advertising Kubernetes Service IPs with Calico and BGP

By [Chris Hoge](https://www.tigera.io/blog/author/chrishoge/) on Apr 01, 2020 • 8 min read



Two of the most common ways of accessing services hosted within a Kubernetes cluster are through an [Ingress](http://kubernetes.io/docs/concepts/services-networking/ingress/) resource or Service type [Load Balancer](http://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer). For users of public clouds, these are simple and effective ways to give access to services. Cloud-hosted controllers can do the heavy lifting of assigning public IPs, setting up load balancing, and managing SSL termination.






Operators running privately-hosted Kubernetes clusters on-premises will quickly realize that making services publicly available is more complicated than on public clouds. Will services be on the public internet, or only public to local users? How should ingress, load balancing, IP-assignment, and SSL termination be set up? What’s the most effective way to make services publicly available while maintaining security?






## Calico Service IP Advertisement



Project Calico offers answers to some of these questions with its *Service IP Advertisement* feature, which integrates with existing Top of Rack (ToR) infrastructure to provide routing to either private Kubernetes Service IPs, or public Kubernetes External Service IPs. Service IP Advertisement is a good solution if:








- Your ToR solution is capable of running the Border Gateway Protocol (BGP)

- You want to share services from your cluster to the rest of your network infrastructure.

- You want to take advantage of network load balancing.






## Enabling the Service IP Advertisement Feature using BGP



To enable Service IP Advertisement Calico needs to peer with a BGP router, one that is external from Calico’s internal routers but local to your network. BGP is one of the most fundamental routing protocols used in networking. At a high level, BGP works by sharing routes between trusted peers. When peered with your ToR, Calico shares routes to Kubernetes services, which makes them available to your entire network.






To help with the discussion of this feature, assume we have a network and cluster configured in the following way:








- A rack of servers, hosting the Kubernetes nodes, are connected to the physical network through a Top of Rack router.

- Calico is running as the CNI and Network Policy plugin.

- Calico and the Top of Rack router are configured to peer together using BGP.






Within this setup, we have the following network configuration:








- The Top of Rack router’s IP address is 192.168.1.1 and the servers have IP addresses allocated from 192.168.1.0/24.

- The Kubernetes pod network is configured with CIDR 10.48.0.0/16.

- The Kubernetes services cluster IP range is configured as 10.49.0.0/16.

- An external service IP range is configured as 192.168.3.0/24.






The exact configuration of your ToR BGP router is beyond the scope of this post, and will vary depending on the vendor or software package you’re using. If you don’t have a rack of servers available to you but still want to try this feature out and see how it works, the [Bird Internet Routing Daemon (BIRD)](http://bird.network.cz/) running on a separate server is a good choice for trying things out.





![Diagram illustrating cloud architecture, showcasing components like servers, storage, and network connections](https://www.tigera.io/app/uploads/2020/12/Calico-Advertise-Service-IP-Diagram-v2.svg)




### Step-by-step Instructions



The first step is to enable peering between the ToR and the Calico network. How this works will vary depending on your ToR implementation, but a few key things to keep in mind are:








- The ToR router will need to be configured to peer with Calico that’s running on each node.

- The ToR needs to accept routes and traffic from the external network, external service network, and pod service network.

- If it’s an option, the ToR should have graceful restarts enabled to prevent network service interruption.






With the ToR configured to accept routes, the next step is to enable peering on the Calico side. Begin by telling Calico about the external BGP router with the following manifest:






```
`calicoctl apply -f - << EOF apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: bgppeer-global-64512 spec: peerIP: 192.168.1.1 asNumber: 64512 EOF`
```






With peering enabled, Calico has made pods first-class citizens on your network, without needing an overlay network, and making them directly accessible outside of the cluster. 






Although peering is enabled, Calico still needs further configuration to expose the Kubernetes service IP range. This can be done by creating a new Calico BGPConfiguration resource:






```
`calicoctl create -f - <<EOF apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: name: default spec: serviceClusterIPs: - cidr: 10.49.0.0/16 EOF`
```






With advertisement of the service cluster IP range enabled, your routing table on your ToR will look something like this:






```
`$ ip r ... 10.49.0.0/16 nexthop via 192.168.1.10 dev eth2 weight 1 nexthop via 192.168.1.11 dev eth2 weight 1 nexthop via 192.168.1.12 dev eth2 weight 1 ...`
```






Note how the routes to the 10.49.0.0/16 network are ECMP load balanced across the nodes. Any services you expose will be load-balanced by the ToR across all of the nodes. To illustrate this, we can create a basic Nginx service.






```
`control:~$ kubectl apply -f - <<EOF apiVersion: apps/v1 kind: Deployment metadata: name: nginx spec: selector: matchLabels: app: nginx replicas: 2 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80 EOF control:~$ kubectl expose nginx control:~$ kubectl get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.49.0.1 <none> 443/TCP 19m nginx ClusterIP 10.49.62.131 <none> 80/TCP 4m43s`
```






From the external network, you can now access the Kubernetes hosted service at its internal cluster IP address.






```
`external:~$ curl 10.49.222.180 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ...`
```






Under the covers there are two stages of load-balancing happening here.  First the ToR load-balanced the connection to a cluster IP by routing it to a node.  Then kube-proxy running on that node load-balanced to a specific pod using NAT (Network Address Translation) to change the destination IP from the cluster IP to the IP address of one of the backing pods. The backing pod might be on the local node, or it might be on one of the other nodes, resulting in another network hop.






If we want to avoid the extra potential network hop, we can do so by setting the external traffic policy on the service to local.






```
`control:~$ kubectl patch service nginx -p '{"spec":{"type": "NodePort", "externalTrafficPolicy":"Local"}}'`
```






On your ToR you will now see the addition to the routing tables, including ECMP load balancing of the Nginx service to the specific nodes on which Nginx is running:






```
`$ ip r ... 10.49.0.0/16 nexthop via 192.168.2.10 dev eth2 weight 1 nexthop via 192.168.2.11 dev eth2 weight 1 nexthop via 192.168.2.12 dev eth2 weight 1 ... 10.49.62.131 nexthop via 192.168.2.11 dev eth2 weight 1 nexthop via 192.168.2.12 dev eth2 weight 1`
```






This is an easy way to expose Kubernetes network services, but operationally it has a drawback of exposing every service on that IP range in your Kubernetes cluster to the rest of your network. If you want more fine-grained control over which services are made available, or want to assign truly public-facing IP addresses, Calico can address this by advertising external service IPs. The method is similar, with a major difference being that external IPs aren’t managed by the Kubernetes cluster and must be assigned manually to the services. The next example will illustrate that. Begin by reconfiguring the BGPConfiguration to advertise the external IPs rather than the internal IPs (it’s worth noting that you can expose both sets at once, but for this example we want to turn off public access to the internal network while still providing access to the application). 






```
`control :~$calicoctl create -f - <<EOF apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: name: default spec: serviceClusterIPs: serviceExternalIPs: - cidr: 192.168.3.0/24 EOF`
```






Checking the routes, note that the routes to the cluster IP range are removed and routes to the external service network have been added. 






```
`$ ip r ... 192.168.3.0/24 nexthop via 192.168.1.10 dev eth2 weight 1 nexthop via 192.168.1.11 dev eth2 weight 1 nexthop via 192.168.1.12 dev eth2 weight 1 ...`
```






The service’s cluster IP is no longer publicly visible.






```
`external :~$ curl -m 10 10.49.62.131 curl: (28) Connection timed out after 10001 milliseconds`
```






We are now advertising the external IP range, but we also need to assign an external IP to the service:






```
`control:~$ kubectl patch svc nginx -p '{"spec": {"externalIPs": ["192.168.3.180"]}}' control:~$ kubectl get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.49.0.1 <none> 443/TCP 152m nginx NodePort 10.49.62.131 192.168.3.180 80:31890/TCP 109m`
```






Check the connectivity:






```
`external:~$ curl 192.168.3.180 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ...`
```






That completes our quick tour of how Calico can advertise Service and External Service IPs outside of a Kuberentes cluster. For on-premises clouds with BGP enabled routing, it’s a simple solution for giving access to Kubernetes services without the additional work of installing and maintaining custom Kubernetes load-balancer or ingress controllers. If you want to read more about this feature, check out the official [Project Calico “Advertise Kubernetes Service IPs” Documentation](http://docs.projectcalico.org/networking/advertise-service-ips). 






Are you using this feature? Do you have any comments or feedback on it or other Calico features? The Project Calico team would love to hear from you. Head on over to our [Slack channel](http://slack.projectcalico.org/), or start a discussion over on the [GitHub Discussions](https://github.com/projectcalico/calico/discussions).








If you enjoyed this blog then you may also like:








- [Introducing the Calico eBPF dataplane](https://www.tigera.io/blog/introducing-the-calico-ebpf-dataplane/) including new service routing options with source IP preservation and DSR (Direct Server Return)

- Free online training at [projectcalico.org/events](http://projectcalico.org/events) or subscribe to [Calico Essentials](https://info.tigera.io/rs/805-GFH-732/images/Calico-Essentials-Datasheet.pdf) for personalized training & workshops

- Learn about [Calico Enterprise](http://docs.projectcalico.org/calico-enterprise/)








[Kubernetes icons](http://github.com/kubernetes/community/tree/master/icons) CC-BY-4.0, copyright 2019 Linux Foundation






References:
[Kubernetes Service IPs](http://kubernetes.io/docs/concepts/services-networking/service/#ips-and-vips)
[Kubernetes External IPs](http://kubernetes.io/docs/concepts/services-networking/service/#external-ips)






 



[How-To](https://www.tigera.io/tags/how-to/)[Open Source](https://www.tigera.io/tags/open-source/)[Project Calico](https://www.tigera.io/tags/project-calico/)

## Related posts

[![Save the Address, Save the Cloud: A Hands-on KubeVirt Live Migration Workshop](https://www.tigera.io/app/uploads/2026/07/Save-the-Address-Save-the-Cloud-A-Hands-on-KubeVirt-Live-Migration-Workshop.png)](https://www.tigera.io/blog/save-the-address-save-the-cloud-a-hands-on-kubevirt-live-migration-workshop/)

#### [Save the Address, Save the Cloud: A Hands-on KubeVirt Live Migration Workshop](https://www.tigera.io/blog/save-the-address-save-the-cloud-a-hands-on-kubevirt-live-migration-workshop/)

By [Reza Ramezanpour](https://www.tigera.io/blog/author/rezar/)
on Jul 9, 2026

In the previous post in this series, we covered why Virtual Machine (VM) Live Migration in Kubernetes is difficult: a VM’s IP is its identity, and the “new” VM on the destination node has to...

[Read more](https://www.tigera.io/blog/save-the-address-save-the-cloud-a-hands-on-kubevirt-live-migration-workshop/)

[![What’s new in Calico: Spring 2026 Release](https://www.tigera.io/app/uploads/2026/06/Whats-New-in-Calico-NEW-TEMPLATE-2026.png)](https://www.tigera.io/blog/whats-new-in-calico-spring-2026-release/)

[Company Blog](https://www.tigera.io/category/company-blog/)

#### [What’s new in Calico: Spring 2026 Release](https://www.tigera.io/blog/whats-new-in-calico-spring-2026-release/)

By [Veronika Smolik](https://www.tigera.io/blog/author/veronika-smolik/)
on Jun 2, 2026

Kubernetes has come a long way since its debut in 2014. It’s gone from running a couple of containerized microservices to orchestrating fleets of production workloads spanning everything from AI agents to full scale VMs...

[Read more](https://www.tigera.io/blog/whats-new-in-calico-spring-2026-release/)

[![Kubernetes Operational Maturity: Secure and Resilient Cluster Federation with Cluster Mesh](https://www.tigera.io/app/uploads/2026/05/Kubernetes-Operational-Maturity-Secure-and-Resilient-Cluster-Federation-with-Cluster-Mesh.png)](https://www.tigera.io/blog/kubernetes-operational-maturity-secure-and-resilient-cluster-federation-with-cluster-mesh/)

#### [Kubernetes Operational Maturity: Secure and Resilient Cluster Federation with Cluster Mesh](https://www.tigera.io/blog/kubernetes-operational-maturity-secure-and-resilient-cluster-federation-with-cluster-mesh/)

By [Veronika Smolik](https://www.tigera.io/blog/author/veronika-smolik/)
on May 25, 2026

Practically no one runs a single Kubernetes cluster in production these days. Maybe that’s how it started but data sovereignty requirements, acquisitions, AI initiatives and the need for edge servers, among other considerations, have pulled...

[Read more](https://www.tigera.io/blog/kubernetes-operational-maturity-secure-and-resilient-cluster-federation-with-cluster-mesh/)

<!-- plugin=object-cache-pro client=phpredis metric#hits=3438 metric#misses=35 metric#hit-ratio=99.0 metric#bytes=1507757 metric#prefetches=163 metric#store-reads=47 metric#store-writes=14 metric#store-hits=171 metric#store-misses=24 metric#sql-queries=31 metric#ms-total=662.88 metric#ms-cache=29.23 metric#ms-cache-avg=0.4872 metric#ms-cache-ratio=4.4 sample#redis-hits=38542617 sample#redis-misses=10825941 sample#redis-hit-ratio=78.1 sample#redis-ops-per-sec=23 sample#redis-evicted-keys=0 sample#redis-used-memory=159586512 sample#redis-used-memory-rss=114987008 sample#redis-memory-fragmentation-ratio=0.7 sample#redis-connected-clients=1 sample#redis-tracking-clients=0 sample#redis-rejected-connections=0 sample#redis-keys=174650 -->
