How Traffic Flows in Kubernetes: A Deep Dive with Diagram (2026)

Updated Jul 2026 · Tested on Kubernetes 1.33, Kubernetes 1.34

Advertisement

Someone opens your app in a browser. A few milliseconds later a container deep inside your cluster gets the request. Between those two moments, the packet passes through half a dozen Kubernetes components, each doing one specific job. This article walks that whole path, step by step, with the commands to inspect each hop yourself.

We will cover both directions of traffic: North-South, which moves between the outside world and the cluster, and East-West, which moves between Pods inside it. Here is the full picture before we break it down.

How a Request Flows in KubernetesThe path one external request takes, from the internet down to a Pod.Client / InternetLoadBalancerpublic IP, forwards to nodesIngress / Gatewayhost/path routing, TLSServiceClusterIP, stable virtual IPkube-proxyrewrites Service IP to Pod IPPod IPone routable IP per PodCNI plugine.g. Cilium, Calico, FlannelNode Network to PodNIC, routes, VXLAN / BGPentry edgeL7 routerabstractionon each nodereal targetpod networkTHE KUBERNETES NETWORK MODEL- Every Pod gets one routable IP- Pods reach each other without NAT- Flat network, every Pod reachable- IPAM and routing handled by the CNI- Service names resolved by CoreDNS
The path a single external request takes through Kubernetes, from the client down to a Pod. Internal Pod-to-Pod traffic joins this same path at the Service stage after a CoreDNS lookup.

The two directions of traffic

Every packet in a Kubernetes cluster is going one of two ways.

North-South traffic crosses the cluster boundary. A user’s browser hitting your website, an external API call, a webhook from a payment provider: all of it enters from outside and has to be routed in through a controlled edge. This is the traffic you secure with TLS, rate limits, and a single well-defined front door.

East-West traffic stays inside. Your web Pod calling your auth Pod, a worker reading from a cache, one microservice talking to another. This traffic never leaves the cluster network, and it moves Pod-to-Pod without going back out through the edge.

The reason this split matters is that the two paths use different machinery. North-South goes through an Ingress or Gateway. East-West goes through service discovery and the flat Pod network. Keep the two directions straight and the rest of Kubernetes networking gets much easier to reason about.

The foundation: the Kubernetes network model

Before following a packet, you need the rules of the field. Kubernetes does not implement networking itself. Instead it defines a model that every network plugin must satisfy, and the model has three demands:

Every Pod gets its own unique IP address. Not a shared node IP with ports, an actual routable IP per Pod. Any Pod can reach any other Pod using that IP, with no network address translation in between. And agents on a node, like kubelet, can reach all Pods on that node.

That flat, NAT-free model is the thing that makes East-West traffic simple. A Pod does not care where another Pod runs. It just uses the IP. The component that makes this real is the CNI plugin, which we will reach at the bottom of the path.

Following an external request: the North-South path

Let’s trace a request from a browser all the way to a Pod.

Step 1: the LoadBalancer and the cluster edge

The request first hits something with a public IP. In a cloud cluster that is usually a cloud load balancer, provisioned automatically when you create a Service of type LoadBalancer. It forwards traffic to the nodes.

kubectl get svc -A --field-selector spec.type=LoadBalancer

Step 2: Ingress or Gateway API routing

The load balancer hands off to the cluster’s edge router. Traditionally this is an Ingress resource paired with an Ingress controller, which does host- and path-based HTTP routing and terminates TLS.

kubectl get ingress -A
kubectl describe ingress my-app

The newer approach is the Gateway API, which reached a major stable milestone with v1.5 in early 2026 and is the designated successor to Ingress. It splits the old single-resource model into roles: a Gateway defines the listener and the edge, and HTTPRoutes define the routing rules. It handles the same north-south job with a cleaner, more expressive model.

kubectl get gateways -A
kubectl get httproutes -A

Ingress still works and is everywhere, but new capability is landing in Gateway API, and some older Ingress controllers are winding down. For a new cluster, Gateway API is the forward-looking pick.

Step 3: the Service

The edge routes to a Service, not directly to a Pod. A Service is a stable abstraction over a set of Pods that come and go. It has a virtual IP, the ClusterIP, that never changes even as the Pods behind it are replaced.

kubectl get svc my-app
kubectl get endpointslices -l kubernetes.io/service-name=my-app

The EndpointSlice is the live list of Pod IPs currently backing the Service. When Pods scale or restart, this list updates, and the Service IP stays put.

Step 4: kube-proxy turns the Service IP into a Pod IP

Here is the part that confuses most people. The ClusterIP is virtual. No network card owns it, nothing is actually listening on it. So how does a packet to that IP reach a real Pod?

That is kube-proxy’s job. It runs on every node and programs the Linux kernel with forwarding rules that catch any packet headed for a Service IP and rewrite the destination to the IP of one real backend Pod, chosen from the EndpointSlice.

kube-proxy can do this in three modes. The classic iptables mode is still the default. The nftables mode became generally available in Kubernetes 1.33 and fixes long-standing performance problems at large scale, though you opt into it explicitly. The older IPVS mode was deprecated in 1.35 and should not be chosen for new clusters.

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

For small or default clusters, iptables is fine. For large clusters on a modern kernel, nftables is the better choice.

Step 5: the Pod IP and the CNI plugin

Now the packet has a real Pod IP as its destination. Getting it there across the node network is the CNI plugin’s responsibility. Calico and Cilium are the two most common. The CNI assigned that Pod its IP when it started, set up the virtual interface linking the Pod to the node, and installed the routes that make the IP reachable.

kubectl get pods -o wide
kubectl get pods -n kube-system | grep -E "calico|cilium"

The -o wide output shows each Pod’s IP and node. That IP came from the CNI’s IP address management, and the route to it was programmed by the CNI too.

Step 6: the node network

Finally the packet rides the actual node network to the destination Pod. If the target Pod is on the same node, this is a local hop through virtual interfaces. If it is on a different node, the CNI carries it across, either through an overlay that wraps the packet in VXLAN, or by plain routing where the CNI advertises Pod routes with BGP so the underlying network knows how to reach each Pod.

That is the whole North-South path: client to load balancer to Ingress or Gateway to Service to kube-proxy to Pod IP to CNI to node network to Pod.

Following an internal request: the East-West path

Now the simpler direction. Say your web Pod needs to call your auth service. This traffic never touches the Ingress or the load balancer.

Service discovery through CoreDNS

The web Pod does not know the auth Pod’s IP, and it should not, because that IP changes. Instead it looks up a name. Kubernetes runs CoreDNS inside the cluster, and every Service gets a DNS name like auth.default.svc.cluster.local.

kubectl get svc -n kube-system kube-dns
kubectl run tmp --rm -it --image=busybox --restart=Never -- nslookup auth.default

CoreDNS returns the auth Service’s ClusterIP. From there the flow rejoins the machinery you already know: the packet to that ClusterIP hits kube-proxy’s rules and gets rewritten to a real auth Pod IP, and the CNI delivers it. Same Service and kube-proxy and CNI layers, just without the external edge on the front.

Network policies

By default every Pod can talk to every other Pod, which the flat network model guarantees. In production you usually want to restrict that. NetworkPolicies let you say which Pods may talk to which, and the CNI enforces them. Not every CNI supports policies, which is one reason Calico and Cilium are popular.

kubectl get networkpolicies -A
kubectl describe networkpolicy default-deny

A common starting point is a default-deny policy that blocks all East-West traffic, then explicit policies that allow only the connections you actually need.

Putting the whole path together

Here is the packet’s journey in one line for each direction.

North-South, external request inbound: Client → LoadBalancer → Ingress or Gateway → Service → kube-proxy → Pod IP → CNI → Node network → Pod.

East-West, Pod to Pod: Pod → CoreDNS lookup → Service ClusterIP → kube-proxy → Pod IP → CNI → destination Pod.

Both paths converge on the same core: a Service gives you a stable address, kube-proxy turns that address into a real Pod, and the CNI moves the packet across the network. Ingress and Gateway sit in front for the outside world, and CoreDNS handles name resolution on the inside.

Do I need to install a CNI plugin myself?

Since the CNI plugin does so much of the work, a fair question is whether you have to install it yourself when you build a cluster. A CNI plugin is always required, a cluster’s Pod networking does not function without one, but whether you install it by hand depends entirely on how you create the cluster.

If you use a managed cloud service, it is already there. Amazon EKS, Google GKE and Azure AKS all ship with a CNI wired in, so you get working Pod networking out of the box. GKE uses a Cilium-based dataplane, AKS offers Azure CNI, and EKS bundles the AWS VPC CNI, though many teams swap that for Calico or Cilium to get stronger network policy support. Lightweight distributions bundle one too. k3s ships Flannel, and Minikube and kind set up a default plugin so local clusters just work.

If you build the cluster yourself with kubeadm, you must install the CNI as a separate step. This is the single most common surprise for people setting up their first cluster. You run kubeadm init, check your nodes, and they sit in the NotReady state. Nothing is broken. kubeadm deliberately does not choose a network plugin for you, and the nodes stay NotReady until you apply one.

kubectl get nodes

If the nodes show NotReady right after cluster init, the fix is to install a CNI, after which they turn Ready within a minute or two. Calico, for example, installs through its Tigera operator (check the Calico docs for the current version string).

kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.32.1/manifests/tigera-operator.yaml
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.32.1/manifests/custom-resources.yaml
kubectl get nodes -w

So the rule of thumb is simple. On a managed cloud cluster or a lightweight distribution, the CNI is bundled and you install nothing extra. On a kubeadm or from-scratch cluster, installing the CNI is a required manual step, and forgetting it is the number one reason a fresh cluster shows NotReady nodes.

Debugging traffic flow

When traffic does not arrive, walk the path in order and check each hop. This ordered checklist mirrors the flow above.

  1. Does the Service have endpoints? Run kubectl get endpointslices for it. No endpoints means no healthy Pods match the selector, and nothing downstream will work.
  2. Are the Pods ready? kubectl get pods -o wide and check the READY column and the Pod IPs.
  3. Is the Ingress or Gateway routing correctly? kubectl describe ingress or kubectl get httproutes and confirm the host and path rules.
  4. Is DNS resolving? Exec into a Pod and nslookup the Service name.
  5. Is a NetworkPolicy blocking it? kubectl get networkpolicies -A. A default-deny with a missing allow rule is a classic cause of silent East-West failures.
  6. Is kube-proxy healthy? kubectl -n kube-system get pods -l k8s-app=kube-proxy and check its logs on the node in question.
  7. Is the CNI healthy? Check the CNI Pods in kube-system. A crashed CNI agent breaks Pod networking on its node.

Working the list top to bottom almost always isolates the failing hop quickly.

Where to go next

Once the traffic path makes sense, the natural next step is the commands to operate it day to day. The Kubernetes commands you must know reference covers the kubectl you will use most. On the automation side, Terraform provisions the cluster and Ansible configures the nodes, and if you are still choosing tools, Ansible vs Terraform vs Puppet vs Chef lays out when to use which.

Advertisement