Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Kubernetes Networking Model Explained

By Kokil Thapa | Last reviewed: September 2026

You deploy an app to Kubernetes and the first networking question hits immediately: why can Pod A reach Pod B on another node without a manual route? The Kubernetes networking model explained in plain terms starts with four rules every cluster must satisfy. Pods get real IPs. Nodes talk to pods directly. Pods talk to each other without NAT. Those rules sit below everything else—Docker networking, Services, Ingress, and NetworkPolicy. If you understand the model first, debugging a CrashLoop or a broken Service becomes a routing problem, not guesswork.

What are the core principles of the Kubernetes networking model?

Kubernetes does not ship a built-in network implementation. It defines requirements and delegates wiring to a Container Network Interface (CNI) plugin. The official model rests on four constraints documented at kubernetes.io/docs/concepts/cluster-administration/networking.

  • Every Pod receives a unique IP address across the cluster.
  • Pods communicate with each other without Network Address Translation.
  • Agents on a node (kubelet, kube-proxy) can reach every Pod on that node.
  • Traffic from a Pod to another Pod follows a flat, routable path.

These rules sound simple. In practice they force a flat L3 network where the Pod CIDR is routable on every node. Your cloud VPC or bare-metal underlay must cooperate. A common mistake is treating Kubernetes like Docker Compose on a single host. Compose relies on bridge NAT. Kubernetes expects pod IPs to behave like real endpoints.

The control plane never forwards application traffic. The API server stores Service and EndpointSlice objects. Data-plane components—CNI, kube-proxy, and optionally an Ingress controller—move packets. That split mirrors how I run Linux server administration on production boxes: separate config from packet forwarding.

Kubernetes Networking Model — Four RulesUnique Pod IPOne IP per Pod, cluster-wideNo NATPod-to-pod direct routingNode Reachabilitykubelet reaches all local PodsFlat NetworkEvery Pod IP is routableNo hidden NAT layerCNI Plugin Implements the Data PlaneCalico · Cilium · Flannel · Weave
Kubernetes networking model explained: four mandatory rules and the CNI layer that implements them

How Pod CIDR and node allocation work

Each node receives a slice of the cluster Pod CIDR at join time. On a typical kubeadm cluster, flags look like this:

--pod-network-cidr=10.244.0.0/16
--service-cidr=10.96.0.0/12

The Pod CIDR holds routable Pod IPs. The Service CIDR holds virtual ClusterIP addresses. They must not overlap with your node network or VPC range. Overlap causes silent blackholes. I've seen teams burn hours on that during a Kubespray deployment because the cloud VPC used 10.0.0.0/16 and the default Pod CIDR collided.

How does pod-to-pod communication work across nodes?

When kubelet starts a Pod, the CNI plugin creates a veth pair. One end sits in the Pod network namespace. The other attaches to a bridge or routes through an overlay tunnel on the host. The plugin assigns an IP from the node's Pod CIDR slice and installs routes so other nodes can reach it.

Cross-node traffic depends on your CNI choice. Flannel often uses VXLAN encapsulation. Calico can run BGP peering for native routing. Cilium with eBPF bypasses iptables for many paths and attaches programs directly at the kernel. The packet path differs. The user-visible behaviour stays the same: Pod IP to Pod IP, no NAT.

Pod-to-Pod Traffic Across NodesPod A10.244.1.5Node 1Pod B10.244.2.8Node 2CNI Overlay / BGPVXLAN · Geneve · Direct RouteNo NAT — source Pod IP preserved end-to-endReturn traffic routes back via CNI
Cross-node pod-to-pod path in the Kubernetes networking model: veth, CNI tunnel, direct routing

Verifying connectivity from inside a Pod

When a Service fails but Pods look healthy, test the data plane directly:

kubectl run nettest --image=nicolaka/netshoot --rm -it -- bash
curl -v http://10.244.2.8:8080/health
nslookup kubernetes.default.svc.cluster.local

If Pod IP works but the Service name fails, the problem sits in kube-proxy or CoreDNS—not the CNI. That isolation saves time. It is the same method I use when debugging CrashLoopBackOff cases where the app cannot reach its database Service.

What is a Kubernetes Service and how does kube-proxy route traffic?

Pods are ephemeral. Their IPs change on every reschedule. A Service provides a stable virtual IP and DNS name. kube-proxy watches EndpointSlice objects and programs rules on each node to forward Service traffic to ready Pod backends.

Four Service types cover most production patterns:

TypeReachable FromTypical UseNotes
ClusterIPInside cluster onlyInternal microservicesDefault type; virtual IP from Service CIDR
NodePortAny node IP + portDev, bare metalOpens 30000–32767 on every node
LoadBalancerExternal via cloud LBPublic APIsCloud provider provisions external LB
ExternalNameDNS CNAME onlyExternal SaaS aliasNo proxying; returns external hostname

On bare metal without a cloud load balancer, MetalLB hands out IPs from a configured pool. That pattern works well for on-prem clusters in Nepal where cloud LBs are unavailable or costly.

ClusterIP forwarding with kube-proxy

A minimal Service and Deployment look like this:

apiVersion: v1
kind: Service
metadata:
  name: api
spec:
  selector:
    app: api
  ports:
    - port: 80
      targetPort: 8080
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: myapp:1.2.0
          ports:
            - containerPort: 8080

kube-proxy supports iptables mode (default on many clusters) and IPVS mode for larger endpoint counts. IPVS scales better beyond a few hundred backends per Service. Check your mode:

kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode

EndpointSlices replaced the older Endpoints API for scalability. Each slice holds up to 100 endpoints. Large Services split across multiple slices. kube-proxy reconciles all of them.

Service → kube-proxy → Pod BackendsClient Podcalls api:80ClusterIP Service10.96.0.42 virtual IPkube-proxyiptables / IPVSEndpointSlicesPod 1:8080Pod 2:8080Pod 3:8080DNAT to ready Pod IP:port — round-robin or session affinity
kube-proxy maps a ClusterIP Service to ready Pod endpoints via iptables or IPVS rules

How do CoreDNS and Ingress fit into Kubernetes networking?

CoreDNS runs as a cluster add-on. It resolves names like api.default.svc.cluster.local to the Service ClusterIP. Pods inherit DNS settings through kubelet. The search path includes svc.cluster.local, so short names like api work inside the same namespace.

Inspect CoreDNS when name resolution fails:

kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50

Ingress sits above Services for HTTP and HTTPS. An Ingress resource defines hostnames, paths, and TLS certificates. An Ingress controller—nginx, Traefik, or cloud-native options—implements the rules. It is not part of the core networking model. It is the standard north-south entry point.

Read the dedicated guide on Ingress controllers for TLS termination and annotation details. For east-west security, pair Services with NetworkPolicies that restrict which Pods may connect.

Headless Services and StatefulSets

A headless Service sets clusterIP: None. DNS returns Pod A records directly instead of a virtual IP. StatefulSets rely on this for stable per-replica hostnames:

apiVersion: v1
kind: Service
metadata:
  name: postgres
spec:
  clusterIP: None
  selector:
    app: postgres
  ports:
    - port: 5432

Clients resolve postgres-0.postgres.default.svc.cluster.local to a specific Pod IP. That pattern suits databases and message brokers where you need identity, not load balancing.

Which CNI plugin should you choose for a production cluster?

The CNI specification at containernetworking/cni SPEC.md defines how plugins configure interfaces. Kubernetes only requires compliance with the four networking rules. Plugin choice affects performance, observability, policy enforcement, and operational complexity.

CNIData PlaneNetworkPolicyBest For
FlannelVXLAN overlayNo (needs Calico addon)Simple clusters, learning
CalicoBGP or overlayYesGeneral production, on-prem BGP
CiliumeBPFYes (extended)High performance, observability
Weave NetOverlay meshYesSmall teams, encryption built-in

For a Laravel API behind nginx on Kubernetes, I usually pick Calico or Cilium. Both support NetworkPolicy. Cilium adds Hubble for flow visibility. Flannel is fine for a local Minikube or kind lab. It lacks native policy support.

Edge clusters on constrained hardware often run K3s with Flannel. Production multi-tenant workloads need policy from day one. Budget time to test pod-to-pod latency and DNS resolution under load. Use a JSON formatter when inspecting EndpointSlice objects from the API.

Full Kubernetes Networking StackIngress Controller (HTTP/S)Service (ClusterIP / LB)kube-proxy + CoreDNSVirtual IP and name resolutionCNI Plugin (Pod Network)NetworkPolicyPod IPsNode Network
Complete Kubernetes networking model explained: Ingress, Service, kube-proxy, DNS, CNI, and policy layers

Common production gotchas

  1. CIDR overlap: Pod, Service, and node ranges must be disjoint. Fix the underlay before joining nodes.
  2. Hairpin NAT: A Pod calling its own Service ClusterIP may fail depending on kube-proxy mode. Call Pod IPs directly in tests or enable hairpin mode.
  3. Readiness probes: Not-ready Pods drop from EndpointSlices. Traffic stops even when the Pod process still runs.
  4. MTU mismatches: Overlay headers shrink effective MTU. Lower Pod interface MTU or raise the physical MTU to avoid silent TCP hangs.
  5. Conntrack exhaustion: High-connection Services on iptables mode can drain conntrack tables. Monitor nf_conntrack_count and consider IPVS or Cilium.

These issues surface during performance tuning more often than in lab clusters. On a booking platform like Adventure Third Pole Trek, dropped connections during peak season would hurt conversions. Network baseline tests belong in your deploy checklist.

If you run PHP/Laravel on Kubernetes, the Laravel on Kubernetes getting started guide covers app-level config. Networking still follows the same model. Sidecar meshes like Istio add another hop. They do not replace CNI or Services.

Compare orchestrators in Kubernetes vs Docker Swarm if your team is still choosing a platform. Swarm uses overlay NAT by default. Kubernetes rejects that pattern for pod-to-pod traffic. The difference matters for observability and security policy.

For API-heavy workloads, stable internal DNS and Service discovery reduce integration bugs. That aligns with how I design REST API development contracts: clients bind to service names, not Pod IPs. The cluster handles the rest.

Understanding worker node architecture helps when you trace where kube-proxy and the CNI binary run. Both are DaemonSets on every node. A single unhealthy node affects local routing for all Pods scheduled there.

Enterprise teams planning multi-cluster setups should read about enterprise application development patterns first. Networking complexity scales with cluster count, not just Pod count.

Key Takeaways

  • Kubernetes requires flat, routable Pod IPs with no NAT—implemented by your CNI plugin, not the API server.
  • Services provide stable ClusterIPs; kube-proxy maps them to ready Pod backends via iptables or IPVS.
  • CoreDNS resolves *.svc.cluster.local names; test Pod IP first when debugging connectivity.
  • Choose Calico or Cilium for production NetworkPolicy; Flannel suits labs and edge only.
  • Verify Pod, Service, and node CIDR ranges do not overlap before joining any node.
  • Ingress handles north-south HTTP traffic; NetworkPolicy controls east-west access between Pods.

People Also Ask

Does Kubernetes use NAT for pod-to-pod traffic?

No. The Kubernetes networking model forbids NAT between Pods. Each Pod keeps its source IP end-to-end. NAT may appear at Ingress or LoadBalancer boundaries for north-south traffic. East-west pod traffic stays direct.

What is the difference between a Pod IP and a Service ClusterIP?

A Pod IP identifies one running container group and changes when the Pod restarts. A ClusterIP is a stable virtual address tied to a Service selector. kube-proxy load-balances connections from the ClusterIP to current ready Pod IPs.

Why can my Pod not reach another Pod on a different node?

Check CNI pod health first, then routing between node networks. Firewall rules blocking overlay ports (VXLAN 4789, Geneve 6081, or BGP 179) cause the most cross-node failures. Confirm both Pod CIDRs are advertised correctly.

Do I need an Ingress controller if I have a LoadBalancer Service?

A LoadBalancer Service exposes one Service on one external IP. Ingress consolidates many HTTP routes and hostnames behind a single entry point with shared TLS. Most production clusters use both: LoadBalancer for the Ingress controller, Ingress rules for individual apps.

Put the Kubernetes networking model to work on your cluster

The Kubernetes networking model explained above boils down to one idea: Pods are first-class network citizens with real IPs. Everything else—Services, DNS, Ingress, NetworkPolicy—builds on that flat foundation. Draw your CIDR map before the first node joins. Pick a CNI with policy support. Test pod-to-pod paths before you deploy apps. Those three steps prevent most production outages I see in the field.

Need help designing cluster networking for a Laravel app, API platform, or multi-service deployment? Contact us to discuss architecture, CNI selection, and a production-ready rollout plan.

Frequently Asked Questions

It is a set of four mandatory rules—unique Pod IPs, no NAT between Pods, node agents reach all local Pods, flat routable pod-to-pod paths—implemented by a CNI plugin, not built into Kubernetes itself.

No. The model forbids NAT between Pods. Each Pod keeps its source IP end-to-end. NAT may appear only at Ingress or LoadBalancer boundaries for north-south traffic.

A Pod IP identifies one container group and changes on restart. A ClusterIP is a stable virtual address from the Service CIDR. kube-proxy forwards traffic from the ClusterIP to ready Pod backends.

Every Pod gets a unique cluster-routable IP. Pods communicate without NAT. Kubelet and kube-proxy can reach every Pod on their node. Pod-to-Pod traffic follows a flat, directly routable L3 path. Kubernetes defines these constraints; your CNI plugin wires the data plane. The control plane stores Service and EndpointSlice objects but never forwards application packets. Treat violations as routing problems, not API server bugs.

Kubernetes ships no network implementation. It delegates interface creation, IP assignment, and routing to a Container Network Interface plugin compliant with the CNI specification. When kubelet starts a Pod, the plugin creates a veth pair, assigns an IP from the node's Pod CIDR slice, and installs routes so other nodes can reach it. Plugin choice affects overlay vs native routing, NetworkPolicy support, and observability.

At cluster setup, you define two non-overlapping ranges. Pod CIDR holds real routable Pod IPs—for example 10.244.0.0/16 on a kubeadm cluster. Service CIDR holds virtual ClusterIP addresses, often 10.96.0.0/12. Each node receives a slice of the Pod CIDR at join time. Neither range may overlap your node network or cloud VPC. Overlap causes silent blackholes I have seen burn hours during Kubespray deployments.

The CNI plugin creates a veth pair per Pod—one end in the Pod namespace, one on the host bridge or tunnel. Cross-node paths depend on the plugin. Flannel often encapsulates in VXLAN. Calico can peer via BGP for native routing. Cilium attaches eBPF programs at the kernel. Packet paths differ, but behaviour stays the same: Pod IP to Pod IP with no NAT and preserved source addresses.

kube-proxy runs on every node as a DaemonSet. It watches EndpointSlice objects and programs forwarding rules so ClusterIP traffic reaches ready Pod backends. Default mode on many clusters is iptables; IPVS scales better beyond a few hundred backends per Service. Not-ready Pods drop from EndpointSlices when readiness probes fail, so traffic stops even if the process still runs. Check mode with kubectl get configmap kube-proxy -n kube-system.

ClusterIP exposes an internal virtual IP for in-cluster microservices—this is the default. NodePort opens ports 30000–32767 on every node IP for dev or bare metal. LoadBalancer provisions a cloud external load balancer for public APIs. ExternalName returns a DNS CNAME to an external hostname with no proxying. On bare metal without cloud LBs, MetalLB assigns IPs from a configured pool—a pattern I use for on-prem clusters where cloud load balancers are unavailable or costly.

CoreDNS runs as a cluster add-on and resolves names like api.default.svc.cluster.local to Service ClusterIPs. Pods inherit DNS settings through kubelet, with search paths including svc.cluster.local so short names work within a namespace. When a Service name fails but direct Pod IP access works, the problem sits in kube-proxy or CoreDNS—not the CNI. Inspect pods and logs in kube-system when name resolution breaks.

They solve different problems. A LoadBalancer Service exposes one Service on one external IP. Ingress consolidates many HTTP routes and hostnames behind a single entry point with shared TLS termination. Most production clusters use both: a LoadBalancer fronts the Ingress controller, and Ingress rules route to individual apps. Ingress sits above Services for north-south HTTP and HTTPS traffic and is not part of the core networking model itself.

Start with CNI pod health on both nodes, then verify routing between node networks. Firewall rules blocking overlay ports cause most cross-node failures—VXLAN 4789, Geneve 6081, or BGP 179 depending on your plugin. Confirm both nodes advertise their Pod CIDR slices correctly. MTU mismatches from overlay headers can also cause silent TCP hangs. Test with kubectl run nettest using nicolaka/netshoot and curl the target Pod IP directly before blaming the application.

Flannel suits local Minikube, kind labs, and edge K3s clusters but lacks native NetworkPolicy. Calico supports BGP or overlay routing with policy enforcement—my usual pick for general production. Cilium uses eBPF for high performance, extended policy, and Hubble flow visibility. Weave Net offers overlay mesh with built-in encryption for small teams. For multi-tenant workloads, pick Calico or Cilium and budget time to test pod-to-pod latency and DNS under load before go-live.

A headless Service sets clusterIP: None. CoreDNS returns Pod A records directly instead of a virtual ClusterIP. StatefulSets rely on this for stable per-replica hostnames like postgres-0.postgres.default.svc.cluster.local resolving to a specific Pod IP. That pattern suits databases and message brokers where clients need pod identity and stable DNS, not load-balanced virtual IPs. It is the standard approach when each replica must be individually addressable.

CIDR overlap between Pod, Service, and node ranges causes silent blackholes—fix the underlay before joining nodes. Hairpin NAT can break a Pod calling its own Service ClusterIP depending on kube-proxy mode. Readiness probe failures remove Pods from EndpointSlices while processes still run. Overlay MTU mismatches shrink effective packet size. High-connection Services on iptables kube-proxy can exhaust conntrack tables—monitor nf_conntrack_count and consider IPVS or Cilium. Network baseline tests belong on every deploy checklist.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: