Understanding Kubernetes: Networking basics

Understanding Kubernetes: Networking basics

In the last article we looked at how Kubernetes builds workloads using controllers: Deployments create ReplicaSets, ReplicaSets manage Pods, and everything is driven by reconciliation.

Now we move one layer lower in the stack: networking. Because up to this point, we’ve mostly ignored how Pods actually communicate with each other. It’s time to change that. Let’s get started.

The core networking assumption

Kubernetes networking is built on a surprisingly strict assumption: Every Pod gets its own IP address and can communicate directly with every other Pod in the cluster. No NAT between Pods. No port forwarding. No node-level address translation. Just flat, routable connectivity between workloads.

Pods and network namespaces

Under the hood, each Pod gets its own Linux network namespace, containing:

  • its own network interfaces
  • its own IP address
  • its own routing table
  • its own loopback interface

All containers inside a Pod share this namespace, which is why they can talk to each other via localhost.

So the model is:

  • inside a Pod → localhost
  • between Pods → cluster network

There is one important exception, though: If hostNetwork: true is set, the Pod does not get its own network namespace and instead shares the node’s network stack. This is rarely used for application workloads, but common in system components.

Kubernetes does not implement networking

One of the most important architectural points: Kubernetes defines the networking model, but does not implement it. The actual networking is handled by a CNI plugin (Container Network Interface).

The CNI is responsible for:

  • assigning Pod IPs
  • attaching network interfaces to Pods
  • configuring routes between nodes
  • ensuring Pod-to-Pod connectivity works

Popular implementations include e.g. Calico, Flannel, or Cilium which all provide the same networking behavior from Kubernetes’ perspective, but implement it differently. Some use overlay networks (VXLAN, IP-in-IP), others use native routing (BGP or cloud routes), and modern systems like Cilium move large parts of this into the Linux kernel itself.

The important point is:

Kubernetes expects a flat Pod network — the CNI makes it real.

Cross-node communication (what actually happens)

Once Pods are scheduled across multiple nodes, something has to connect those networks.

There are two common models:

Overlay networks

In overlay-based setups, Pod traffic is encapsulated before leaving the node:

Pod packet → encapsulated (VXLAN / IP-in-IP) → sent over node network → decapsulated on target node → delivered to destination Pod

This makes the underlying infrastructure simpler, because it only sees node-to-node traffic.

The downside is overhead from encapsulation and MTU considerations.

Routed networks

In routed setups, nodes exchange routing information directly:

via BGP cloud route tables or explicit L3 routing configuration

Here, Pod CIDRs are directly reachable across nodes without encapsulation.

This is more efficient, but requires tighter integration with the underlying network.

From an application perspective, none of this matters:

Pod A → Pod B

is always the same mental model.

Services: where Kubernetes stops exposing raw Pods

At this point there is a subtle but important problem.

Pods are:

ephemeral rescheduled replaced during rollouts frequently assigned new IPs

So while Pod-to-Pod communication works, relying on Pod IPs is not stable.

This is where Services come in.

A Service provides:

a stable virtual IP a stable DNS name load balancing across matching Pods

Example:

apiVersion: v1 kind: Service metadata: name: backend spec: selector: app: backend ports: - port: 80 targetPort: 8080

Now instead of targeting individual Pods:

curl http://10.244.2.7

we can rely on:

curl http://backend

The key idea is:

Services decouple communication from Pod lifecycles.

kube-proxy and Service routing

A Service is not a process. It does not “run” anywhere.

So something still needs to implement:

Service IP → actual Pod IP

Traditionally, this is done by kube-proxy.

It runs on every node and programs network rules using:

iptables or IPVS

When traffic hits a Service IP:

the kernel matches the Service rule kube-proxy rules select a backend Pod traffic is forwarded to that Pod IP

This is also the layer where load balancing across replicas happens.

In modern clusters, this layer is increasingly replaced or bypassed by eBPF-based dataplanes like Cilium, which move service routing into the kernel more efficiently.

A quick reality check

You can verify the model with two Pods:

kubectl run nginx –image=nginx kubectl run toolbox –image=nicolaka/netshoot -it –rm – sh

Then:

kubectl get pods -o wide

From inside the toolbox Pod:

curl http://

This is the simplest proof of the model:

no Service involved yet no DNS no kube-proxy abstraction

Just raw Pod networking across nodes.

The mental model that matters

If you strip everything down, Kubernetes networking is really just three layers:

Pods get real IPs inside a flat network the CNI makes cross-node connectivity possible Services add stable identity on top of volatile endpoints

Everything else builds on top of this.

Once this model is clear, the next abstractions stop feeling arbitrary.

Looking ahead

Networking in Kubernetes only starts to make sense when you see it as a layered system.

In the next article we’ll move one layer up and focus on:

Services, DNS & Service Discovery

This is where ClusterIP Services, selectors, and endpoints start to matter — and where CoreDNS quietly becomes one of the most important components in the entire cluster.

After that, we’ll go outward:

Ingress, Gateway API & External Traffic

This is the north-south traffic layer — where Kubernetes stops being “internal cluster networking” and starts acting like a real application edge with load balancers, reverse proxies, and routing rules.

And finally, we’ll close the networking series with something intentionally practical:

Network Policies

Because once everything can talk to everything, the real question becomes what shouldn’t be able to talk at all.

header image created by buddy ChatGPT