On this page
Kubernetes networking model
The foundational network guarantees of Kubernetes, where every Pod gets a unique IP and can communicate without NAT.
What it is
The Kubernetes networking model defines a strict set of rules for how containers communicate within a cluster. It diverges significantly from the default Docker networking model. Instead of hiding containers behind the host node’s IP address using complex port-mapping and Network Address Translation (NAT), Kubernetes mandates a flat, cluster-wide network space.
The model enforces three fundamental requirements:
- Every Pod gets its own unique IP address.
- Pods on a node can communicate with all Pods on all other nodes without NAT.
- Agents on a node (like system daemons or the kubelet) can communicate with all Pods on that node.
Why it matters
By giving every Pod a real, routable IP address within the cluster, Kubernetes drastically simplifies distributed application development. Applications running inside a Pod do not need to know they are containerized. From their perspective, they are running on a virtual machine with a dedicated network interface.
This eliminates the “port collision” problem entirely. You can run fifty web servers on a single physical node, and they can all bind to port 80 inside their respective Pods, because each Pod has a distinct IP address. It also allows legacy applications, which often pass their own IP addresses in payloads (like SIP or older clustering protocols), to function flawlessly without NAT breaking the internal routing logic.
How to implement
Kubernetes does not implement this network model itself. It relies on third-party plugins that adhere to the Container Network Interface (CNI) specification. Common CNI providers include Calico, Flannel, Cilium, and cloud-provider-specific plugins (like AWS VPC CNI).
When a Pod is scheduled onto a node, the kubelet calls the CNI plugin. The CNI plugin configures the Linux network namespace for the Pod, assigns it an IP address from a pre-allocated subnet, and wires up the virtual ethernet interfaces (veth pairs) to connect the Pod to the host’s routing table.
Network Policies
Because the default model allows all Pods to communicate with all other Pods freely, security requires explicitly locking down traffic. This is done using the NetworkPolicy API object, which acts as an internal, distributed firewall.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {} # Selects all pods in the namespace
policyTypes:
- Ingress
This policy drops all incoming traffic to all Pods in the namespace unless another policy explicitly permits it.
Common mistakes
Relying on Pod IPs
Because every Pod has a unique IP, operators sometimes attempt to hardcode those IPs into configuration files or rely on them for long-term routing. Pod IPs are highly ephemeral. If a node is rebooted, the Pod is recreated with a completely different IP address. Internal communication must always route through Service objects and DNS, which abstract away the shifting Pod IPs.
IP Exhaustion
In some cloud environments (like AWS EKS using the VPC CNI), Pod IPs are drawn directly from the underlying cloud Virtual Private Cloud (VPC) subnet. If you deploy thousands of small Pods, you will rapidly exhaust the available IP addresses in your cloud subnets, preventing new nodes from booting and causing cluster-wide deadlocks. Capacity planning must account for massive IP address consumption.
Ignoring Egress Rules
Many teams implement strict ingress network policies (blocking incoming traffic) but leave egress (outgoing traffic) entirely open. A compromised container can then reach out to external command-and-control servers or scan internal corporate networks. Zero-trust networks require strict egress policies detailing exactly which external IPs and internal services a Pod is allowed to query.
Verification
To verify the flat network model, launch two Pods on completely different physical nodes and test direct communication.
Find the IP address of the destination Pod:
kubectl get pod web-server -o wide
Exec into a source Pod running on a different node and ping the destination IP:
kubectl exec -it debug-pod -- ping 10.244.1.5
The ping must succeed without NAT. If you inspect the network packets using tcpdump, the source IP address visible to the destination Pod must be the exact, un-translated IP address of the source Pod.