---
title: "Topology Aware IP Address Management for Kubernetes"
source: "https://www.tigera.io/blog/topology-aware-ip-management/"
---

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

# Topology Aware IP Address Management for Kubernetes

By [Chris Hoge](https://www.tigera.io/blog/author/chrishoge/) on May 07, 2020 • 5 min read

## Introduction






If you have workloads that are spread across multiple regions, zones, or racks, you may want to assign addresses from IP pools divided amongst the resource boundaries. Restricting IP addresses based on node topology can be useful if you want to reduce the number of routes required in a network or conform with enterprise network security policies.






One of the ways that you can express physical topology in Kubernetes is through labels. Labels are a fundamental mechanism for organizing resources inside of a Kubernetes cluster. By assigning a key-value pair to a resource, you can filter, select, modify, and constrain resources based on operational meaning. While labels are frequently associated with workloads and pods, they are also useful for describing the physical layout and configuration of nodes in a cluster.






Calico provides an IPPool resource that when used with topology described by node labels, lets you assign specific IP pools to nodes with specific labels. Although you can configure IP addresses for nodes in the CNI configuration file, it requires modifications to the host’s file system. Calico’s IPPool resource provides a more elegant and maintainable way to manage allocation of IP addresses.






## IP Assignment Example






To illustrate how this works, consider an example where a cluster of four nodes is spread across two racks.







![Diagram showing IP assignment with a router connected to Rack 0 (kube-node-0, kube-node-1) and Rack 1 (kube-node-2,](https://www.tigera.io/app/uploads/2020/05/rack-996x1024.png)






For the purposes of this example, assume that we’re working with the service IP range of 192.168.0.0/16. We will assign the subnets of 192.168.0.0/24 to rack 0, and 192.168.1.0/24 to rack 1.






By default a kubeadm or similar installation will assign a single IP Pool across the entire cluster. You can verify this with calicoctl:






```
`$ calicoctl get ippool -o wide NAME CIDR NAT IPIPMODE DISABLED SELECTOR default-ipv4-ippool 192.168.0.0/16 true Always false all()`
```






We won’t be able to assign the subnets to the racks while the default-ipv4-ippool resource exists, since the /16 network completely contains the /24 networks. To correct this, start by deleting the default pool. Note that any workloads that already exist and have IP addresses assigned will not be changed. You will need to restart these workloads manually for changes to take effect.






```
`$ calicoctl delete ippools default-ipv4-ippool`
```






Next, label the nodes. To assign IP pools to specific nodes, these nodes must be labelled using [kubectl label](http://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/#add-a-label-to-a-node).






```
`$ kubectl label nodes kube-node-0 rack=1 $ kubectl label nodes kube-node-1 rack=1 $ kubectl label nodes kube-node-2 rack=2 $ kubectl label nodes kube-node-3 rack=2`
```






Create an IP pool for each rack.






```
`$ calicoctl create -f -<<EOF apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: rack-0-ippool spec: cidr: 192.168.0.0/24 ipipMode: Always natOutgoing: true nodeSelector: rack == "0" EOF $ calicoctl create -f -<<EOF apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: rack-1-ippool spec: cidr: 192.168.1.0/24 ipipMode: Always natOutgoing: true nodeSelector: rack == "1" EOF`
```






We should now have two enabled IP pools, which we can see when running:






```
`$ calicoctl get ippool -o wide NAME CIDR NAT IPIPMODE DISABLED SELECTOR rack-1-ippool 192.168.0.0/24 true Always false rack == "0" rack-2-ippool 192.168.1.0/24 true Always false rack == "1"`
```






To verify that the IP pool node selectors are being respected we will create an nginx deployment with five replicas to get a workload running on each node.






```
`$ kubectl run nginx --image nginx --replicas 5`
```






Check that the new workloads now have an address in the proper IP pool allocated for the rack that the node is on with:






```
`$ kubectl get pods -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-5c7588df-prx4z 1/1 Running 0 6m3s 192.168.0.64 kube-node-0 <none> <none> nginx-5c7588df-s7qw6 1/1 Running 0 6m7s 192.168.0.129 kube-node-1 <none> <none> nginx-5c7588df-w7r7g 1/1 Running 0 6m3s 192.168.1.65 kube-node-2 <none> <none> nginx-5c7588df-62lnf 1/1 Running 0 6m3s 192.168.1.1 kube-node-3 <none> <none> nginx-5c7588df-pnsvv 1/1 Running 0 6m3s 192.168.1.64 kube-node-2 <none> <none>`
```






The grouping of IP addresses assigned to the workloads differ based on what node that they were scheduled to. Additionally, the assigned address for each workload falls within the respective IP pool that selects the rack that they run on.






Note that it’s important to assign at least one IP Pool to every node. Nodes will only assign workload addresses from IP pools which select them. If a node doesn’t have an IPPool assigned, a workload may not get an IP and fail to start. One mitigation for this potential problem is to create a default IP Pool with the default selector for all(). Then any node without a label that matches a location-specific pool will be captured by the default pool.






## Summary






The pairing of Calico’s ability to manage IP Pools with Kubernetes node labeling gives you a means for allocating IP ranges based on physical infrastructure layout, whether that layout is as fine-grained as a rack or spread out across multiple regions. You can read more about this feature in the [Project Calico How-To guide](http://docs.projectcalico.org/networking/assign-ip-addresses-topology). You can catch up with the Calico team over on our [Slack Channel](http://slack.projectcalico.org/), and get help over in the [GitHub Discussions](https://github.com/projectcalico/calico/discussions). For up-to-date information on new blog posts and community meetings, head on over to [@projectcalico on Twitter](http://twitter.com/projectcalico).








If you enjoyed this blog then you may also like:








- [Calico IPAM: Explained and Enhanced](https://www.tigera.io/blog/calico-ipam-explained-and-enhanced/)

- Free online[training workshops](https://www.tigera.io/events/)

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






 



[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=6174 metric#misses=18 metric#hit-ratio=99.7 metric#bytes=2209705 metric#prefetches=0 metric#store-reads=411 metric#store-writes=18 metric#store-hits=422 metric#store-misses=7 metric#sql-queries=34 metric#ms-total=1365.13 metric#ms-cache=66.15 metric#ms-cache-avg=0.1546 metric#ms-cache-ratio=4.9 sample#redis-hits=50888529 sample#redis-misses=8178675 sample#redis-hit-ratio=86.2 sample#redis-ops-per-sec=227 sample#redis-evicted-keys=0 sample#redis-used-memory=96019592 sample#redis-used-memory-rss=87961600 sample#redis-memory-fragmentation-ratio=0.9 sample#redis-connected-clients=1 sample#redis-tracking-clients=0 sample#redis-rejected-connections=0 sample#redis-keys=46157 -->
