Right, let’s settle this. You’ve seen --service-cidr and --pod-cidr flags thrown around, and if you’re anything like me, you initially thought, “A CIDR is a CIDR, what’s the big deal?” I’m here to tell you the distinction is one of the most fundamental, yet initially confusing, parts of Kubernetes networking. Mixing them up is like confusing the highway (Pod CIDR) with the highway’s on-ramp signage system (Service CIDR). One carries the actual traffic, the other is a brilliant abstraction to find that traffic.

Here’s the core, simple difference: the Pod CIDR is for actual, routable pod IP addresses. The Service CIDR is for virtual, cluster-internal Service IP addresses (VIPs). One is reality, the other is a magnificent, distributed fiction maintained by kube-proxy and the control plane.

Pod CIDR: The Real Estate of Your Cluster

This is the IP address block from which every single pod on a node gets its actual, honest-to-goodness IP address. When you install a CNI plugin like Calico or Flannel, its primary job is to manage this block. It carves out a smaller slice (a /24 or /26, typically) for each node and then handles the actual plumbing: creating virtual Ethernet pairs, putting one end in the pod’s network namespace, attaching the other to a bridge, and setting up routes so that a packet for 10.244.12.5 (a pod on Node A) knows how to get to 10.244.13.10 (a pod on Node B).

You can see this in action. Let’s say your pod CIDR is 10.244.0.0/16. Your pods will live there.

kubectl get pods -o wide
NAME    READY   STATUS    IP           NODE
web-1   1/1     Running   10.244.2.5   node-01
db-1    1/1     Running   10.244.1.10  node-02

These IPs are what the pod itself sees (hostname -I inside the pod), and crucially, they are what the underlying network fabric (routers, cloud VPCs, etc.) needs to be able to route between. This is the “real” network.

Service CIDR: The Cluster’s Phonebook

Now, pods are miserably ephemeral. They die, they get rescheduled, their IPs change. You cannot rely on them. This is where Services come in. The Service CIDR (10.96.0.0/12 by default) is a block of IPs reserved for these virtual Service objects.

When you create a Service named my-svc, the API server’s allocator picks an IP from this block, let’s say 10.96.105.20, and assigns it to the Service. This IP doesn’t correspond to any network interface. No pod “has” this IP. It’s a complete fiction. Its entire existence is inside the iptables rules (or IPVS tables) that kube-proxy manages on every single node.

Here’s the magic. When you curl 10.96.105.20 from any pod in the cluster, kube-proxy’s rules jump in, perform a DNAT (Destination Network Address Translation), and change the destination IP from the virtual 10.96.105.20 to the actual IP of a healthy pod, like 10.244.2.5.

kubectl get svc
NAME         TYPE        CLUSTER-IP      PORT(S)
my-svc       ClusterIP   10.96.105.20    80/TCP

Run iptables -t nat -L on a worker node and you’ll see the glorious, verbose evidence of this trickery. The Service CIDR is just a reserved pool of IPs we’ve all agreed to use for this specific kind of lie.

The Critical Interaction: Why They Must Be Different

This is non-negotiable: your Pod CIDR and your Service CIDR must not overlap. Why? Because the entire system’s logic depends on being able to distinguish between a “real” IP (a pod) and a “virtual” IP (a service).

If you were daft enough to set both to 10.244.0.0/16, the system would have a catastrophic identity crisis. kube-proxy would see a packet destined for 10.244.1.10 and think, “Is this a Service IP I need to redirect? Or a Pod IP I should leave alone?” It can’t tell. The result is that your cluster networking would simply shatter. Traffic would be misrouted or dropped in deeply confusing ways. I’ve seen it happen in a lab, and it’s a special kind of debugging hell. Just don’t.

Best Practices and The Cloudy Edge

First, the obvious: stick to the RFC 1918 private address spaces (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) for both. You’re not a public cloud provider; don’t try to use public IPs here.

Now, the sneaky bit: in managed Kubernetes services like EKS, GKE, or AKS, you often don’t set the Pod CIDR yourself. The cloud provider’s CNI plugin does. It integrates with the cloud’s own networking layer (e.g., AWS VPC CNI gives pods real VPC IPs). You do often get to choose the Service CIDR, however. So the classic pitfall is picking a Service CIDR that overlaps with your company’s internal network or your VPC range. If your office network is 10.10.0.0/16 and your Service CIDR is 10.10.100.0/24, good luck reaching your internal wiki from inside a pod. The kube-proxy rules will try to resolve your wiki’s IP as if it were a Kubernetes Service and fail spectacularly. Always know your upstream network topology.

The fix is planning. Before you run kubeadm init or click that “create cluster” button, map it out. A common, safe pattern is to use a /16 for Pods (e.g., 10.244.0.0/16) and a separate, smaller /16 or /12 for Services (e.g., 10.96.0.0/12). This keeps everything clean, separate, and, most importantly, predictable. The brilliance of Kubernetes networking isn’t in the complexity, but in how it uses these two simple, separate concepts to build a robust and discoverable system from the chaos of constantly changing containers.