DEV Community

Cover image for Kubernetes Networking [Level-1: Pod Networking]
ADITYA RAJ
ADITYA RAJ

Posted on

Kubernetes Networking [Level-1: Pod Networking]

This is Level 1 of our Kubernetes networking series. In Level 0, we built a foundation of core Linux networking concepts — IP addresses, ports, network namespaces, veth pairs, and bridges. Now it's time to connect that foundation directly to Kubernetes.

The central question for this article is deceptively simple:

When Kubernetes creates a Pod, how does that Pod actually get a network interface and an IP address?

By the end of this guide, you'll be able to trace the full journey — from kubectl run to a running Pod with a working eth0 — and understand exactly which component is responsible for each step.

Table of Contents

  1. The Problem Kubernetes Has
  2. The Solution: CNI
  3. Every Pod Gets Its Own Network Namespace
  4. Inspecting the Pod's eth0
  5. Where Does eth0 Connect? The veth Pair
  6. Why We Need the veth Pair
  7. Multiple Pods on One Node
  8. Where Does the Pod IP Come From?
  9. Understanding Pod CIDR
  10. Node CIDR vs Pod CIDR
  11. The Complete Pod Networking Stack
  12. What Happens When a Pod Is Created
  13. The Pause Container: Kubernetes' Hidden Detail
  14. Multiple Containers, One Network Namespace
  15. Port Collisions Inside a Pod
  16. Pod-to-Pod Communication (Same Node)
  17. Pods on Different Nodes
  18. The Kubernetes Networking Model
  19. What CNI Actually Does: A Preview
  20. Hands-On: Inspecting a Real Pod
  21. Level 1 Mental Model
  22. Level 1 Checkpoint
  23. What's Next: Pod-to-Pod Communication

The Problem Kubernetes Has

Picture a bare Kubernetes Node with no Pods running yet:

Node
┌─────────────────────────────────────┐
│        Kubernetes Node              │
│              ???                    │
└─────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Now Kubernetes wants to run a Pod:

Pod
┌─────────────────┐
│ nginx           │
│ eth0            │
│ 10.244.1.5      │
└─────────────────┘
Enter fullscreen mode Exit fullscreen mode

Here's the catch: who creates eth0? Who assigns the Pod its IP, 10.244.1.5? And who connects that interface to the Node's network?

Linux doesn't do any of this automatically just because Kubernetes decided to create a Pod. We need a dedicated networking component to make it happen.

The Solution: CNI

This is where the Container Network Interface (CNI) comes in. Think of CNI as:

The component responsible for connecting a newly created Pod to the network.

Simplified, the flow looks like this:

Kubernetes
    | "I created a Pod."
    ↓
Container Runtime
    | "Pod needs networking."
    ↓
CNI
    ├── Create network namespace
    ├── Create veth pair
    ├── Put one side inside the Pod
    ├── Connect the other side to the Node
    ├── Assign an IP
    └── Configure routes
Enter fullscreen mode Exit fullscreen mode

We'll go much deeper into CNI implementations later in this series (Level 7). For now, just remember its role: CNI is what gives Pods networking.

Every Pod Gets Its Own Network Namespace

Remember network namespaces from Level 0? A Kubernetes Pod gets its own network namespace, giving it an isolated networking environment:

Node
┌─────────────────────────────────────────┐
│   Pod Network Namespace                 │
│   ┌───────────────────────────────┐     │
│   │   eth0                        │     │
│   │   10.244.1.5                  │     │
│   └───────────────────────────────┘     │
└─────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

This isolation is exactly what allows dozens of Pods to run on the same Node without their networking colliding.

Inspecting the Pod's eth0

Inside a running Pod, running:

ip addr
Enter fullscreen mode Exit fullscreen mode

typically shows something like:

1: lo
2: eth0@if123
    inet 10.244.1.5/24
Enter fullscreen mode Exit fullscreen mode

The important takeaway: the Pod has its own network interface (eth0) and its own IP address (10.244.1.5).

Where Does eth0 Connect? The veth Pair

This is where our Level 0 knowledge pays off. A Pod's eth0 is typically connected to the Node using a veth pair — the same mechanism we covered earlier.

                 Kubernetes Node
┌─────────────────────────────────────────────┐
│  Pod namespace                              │
│  ┌────────────────────┐                     │
│  │ eth0               │                     │
│  │ 10.244.1.5         │                     │
│  └─────────┬──────────┘                     │
│            │                                │
│          veth                               │
│            ║                                │
│          veth                               │
│            │                                │
│            ↓                                │
│       Node network                          │
└─────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Think of it as a virtual cable: one end (eth0) lives inside the Pod, and the other end (veth) lives on the Node.

Why We Need the veth Pair

The Pod is isolated inside its own network namespace, which means it has no natural connection to the Node's namespace:

Pod namespace
     |
     | ???
     |
Node namespace
Enter fullscreen mode Exit fullscreen mode

The veth pair bridges that gap:

Pod namespace
     |
   eth0
     |
   veth
     ║
   veth
     |
Node namespace
Enter fullscreen mode Exit fullscreen mode

In short, the veth pair is the virtual cable that connects an isolated Pod to the rest of the Node's networking stack.

Multiple Pods on One Node

Now imagine three Pods running on the same Node:

Pod A → 10.244.1.5
Pod B → 10.244.1.6
Pod C → 10.244.1.7
Enter fullscreen mode Exit fullscreen mode

Conceptually, the Node might look like this:

                    Node
┌────────────────────────────────────────────┐
│ Pod A (10.244.1.5)                         │
│    │                                       │
│   veth ───────────┐                        │
│                    ↓                       │
│                 Bridge                     │
│                    ↑                       │
│   veth ───────────┘                        │
│    │                                       │
│ Pod B (10.244.1.6)                         │
│                                            │
│ Pod C (10.244.1.7)                         │
└────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

The exact implementation varies by CNI plugin, but the mental model stays consistent:

Pod → eth0 → veth → Node networking
Enter fullscreen mode Exit fullscreen mode

Where Does the Pod IP Come From?

Who decided Pod A should get 10.244.1.5? The networking implementation allocates IPs from a defined address range.

For example, a cluster might use a Pod CIDR of 10.244.0.0/16, split into smaller ranges per Node:

Node 1: 10.244.1.0/24
Node 2: 10.244.2.0/24
Node 3: 10.244.3.0/24
Enter fullscreen mode Exit fullscreen mode

So on Node 1:

Pod A → 10.244.1.5
Pod B → 10.244.1.6
Pod C → 10.244.1.7
Enter fullscreen mode Exit fullscreen mode

This is just one example architecture — different CNIs and cluster configurations allocate ranges differently, so don't memorize these exact numbers.

Understanding Pod CIDR

A Pod CIDR is the IP address range allocated for all Pods in the cluster. For example, 10.244.0.0/16 represents a large address space reserved specifically for Pod IPs:

Cluster
└── Pod Network
       ├── 10.244.1.x
       ├── 10.244.2.x
       ├── 10.244.3.x
       └── ...
Enter fullscreen mode Exit fullscreen mode

Again, actual ranges vary by cluster and CNI plugin — what matters is understanding the concept.

Node CIDR vs Pod CIDR

This distinction trips up a lot of beginners. Consider:

Node IP: 192.168.1.10
Pod IP:  10.244.1.5
Enter fullscreen mode Exit fullscreen mode

These belong to entirely different networks:

Node Network: 192.168.1.0/24
       ↓
Pod Network:  10.244.0.0/16
Enter fullscreen mode Exit fullscreen mode

Don't assume a Pod's IP belongs to the same subnet as its Node's IP — in most Kubernetes setups, it doesn't. This is one of the key ideas that unlocks the rest of Kubernetes networking.

The Complete Pod Networking Stack

Putting it all together:

                     Kubernetes Node
┌───────────────────────────────────────────────┐
│  Pod Network Namespace                        │
│  ┌───────────────────────────┐                │
│  │ Container                 │                │
│  │ Application               │                │
│  │     ↓                     │                │
│  │   eth0 (10.244.1.5)       │                │
│  └────────────┬──────────────┘                │
│               │                               │
│             veth                              │
│               ║                               │
│             veth                              │
│               │                               │
│               ↓                               │
│        Node networking                        │
│               │                               │
│               ↓                               │
│         Physical NIC                          │
└───────────────┼───────────────────────────────┘
                ↓
             Network
Enter fullscreen mode Exit fullscreen mode

What Happens When a Pod Is Created

Let's walk through the sequence triggered by:

kubectl run nginx --image=nginx
Enter fullscreen mode Exit fullscreen mode
  1. Kubernetes receives the request.
  2. The Pod gets scheduled onto a Node.
  3. The container runtime prepares the Pod.
  4. Networking gets configured:
Pod created
    ↓
Network namespace created
    ↓
CNI called
    ↓
veth pair created
    ↓
Pod gets eth0
    ↓
IP assigned
    ↓
Routes configured
    ↓
Pod networking ready
    ↓
Container starts
Enter fullscreen mode Exit fullscreen mode

This sequence is one of the most important things to internalize in this article.

The Pause Container: Kubernetes' Hidden Detail

Here's a subtlety many tutorials skip: Kubernetes doesn't give every container its own network namespace — it gives every Pod one shared namespace, owned by a special pause container.

Pod
┌─────────────────────────────────┐
│  Pause container                │
│       └── Pod network namespace │
│                                 │
│  nginx container                │
│  sidecar container              │
└─────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Every container in the Pod shares the Pod's:

  • network namespace
  • IP address
  • network interfaces
  • ports

Multiple Containers, One Network Namespace

Consider a Pod with three containers:

Pod
┌────────────────────────────────────┐
│  Container A                       │
│  Container B                       │
│  Container C                       │
│                                    │
│  Shared network namespace          │
│  eth0 → 10.244.1.5                 │
└────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

All three containers share 10.244.1.5 and can talk to each other over localhost:

Container A → localhost:8080 → Container B
Enter fullscreen mode Exit fullscreen mode

...provided Container B is actually listening on that port.

Port Collisions Inside a Pod

Because containers in a Pod share a network namespace, this can happen:

Container A → :8080
Container B → :8080   ❌
Enter fullscreen mode Exit fullscreen mode

This is a real conflict — two processes can't simultaneously own 10.244.1.5:8080. This is a great example of why understanding raw Linux networking (from Level 0) directly explains real Kubernetes behavior.

Pod-to-Pod Communication (Same Node)

Now suppose Pod A (10.244.1.5) and Pod B (10.244.1.6) are on the same Node:

Pod A (10.244.1.5)
    ↓
  veth
    ↓
Node networking
    ↓
  veth
    ↓
Pod B (10.244.1.6)
Enter fullscreen mode Exit fullscreen mode

The exact mechanism depends on the CNI plugin in use — we'll dig into specific implementations in Level 2 and Level 7.

Pods on Different Nodes

Here's where things get genuinely interesting. Consider two Pods on two different Nodes:

Node 1                         Node 2
Pod A: 10.244.1.5              Pod B: 10.244.2.5
Enter fullscreen mode Exit fullscreen mode

Pod A wants to reach Pod B directly by IP:

┌──────────── Node 1 ────────────┐
│ Pod A (10.244.1.5)             │
└──────────────┬─────────────────┘
               │
               │ Network
               │
┌──────────────▼─────────────────┐
│ Node 2                         │
│ Pod B (10.244.2.5)             │
└────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

This raises a critical question: how does traffic from Pod A reach a Pod IP living on a completely different machine? That question is exactly where Level 2 of this series picks up.

The Kubernetes Networking Model

Kubernetes enforces an important networking guarantee:

Pod A → (direct Pod IP) → Pod B
Enter fullscreen mode Exit fullscreen mode

Pods must be able to communicate across Nodes without the application needing to know or care which Node they're running on. The underlying networking implementation is responsible for making this transparent, typically using one of:

  • Overlay networking
  • Routing-based networking
  • Encapsulation (e.g., VXLAN)
  • Cloud-native networking
  • eBPF-based networking

We'll explore each of these approaches later in the series.

What CNI Actually Does: A Preview

By now you have a much clearer picture of what "CNI" really means. When a Pod is created, the CNI plugin handles:

CNI
 ├── Network namespace
 ├── veth pair
 ├── Pod interface
 ├── IP address
 ├── Routes
 └── Connectivity
Enter fullscreen mode Exit fullscreen mode

Don't worry about specific CNI implementations yet — that's reserved for Level 7. For now: CNI is the mechanism that gives Pods networking.

Hands-On: Inspecting a Real Pod

Let's verify everything we've learned with real commands.

Create a Pod:

kubectl run nginx --image=nginx
Enter fullscreen mode Exit fullscreen mode

Check its IP and Node:

kubectl get pod -o wide
Enter fullscreen mode Exit fullscreen mode
NAME    READY   STATUS    IP            NODE
nginx   1/1     Running   10.244.1.5    node-1
Enter fullscreen mode Exit fullscreen mode

You now have a direct mapping: Pod name → Pod IP → Node.

Check the interface inside the Pod:

kubectl exec -it nginx -- ip addr
Enter fullscreen mode Exit fullscreen mode
lo
eth0@if...
    inet 10.244.1.5/...
Enter fullscreen mode Exit fullscreen mode

This confirms the chain: Pod → network namespace → eth0 → Pod IP.

Check the Pod's routing table:

kubectl exec -it nginx -- ip route
Enter fullscreen mode Exit fullscreen mode
default via ...
10.244.1.0/24 dev eth0
Enter fullscreen mode Exit fullscreen mode

The exact output depends on your CNI plugin, but the key question the routing table answers is always the same: where does the Pod send traffic when it wants to reach something?

Note: At this point you might come across implementation-specific interfaces like docker0, cni0, flannel.1, vxlan.calico, cilium_host, or cilium_net. Don't let these confuse you — they're all specific implementations of the same abstraction: Pod → namespace → eth0 → veth → Node networking → CNI.

Level 1 Mental Model

You should now be able to visualize a Node running multiple Pods like this:

                    NODE
┌────────────────────────────────────────────┐
│  Pod A                                     │
│  ┌─────────────────────────────┐           │
│  │ Network Namespace           │           │
│  │ eth0 (10.244.1.5)           │           │
│  └─────────────┬───────────────┘           │
│                │                           │
│              veth                          │
│                ║                           │
│                ↓                           │
│         Node networking                    │
│                                            │
│  Pod B                                     │
│  ┌─────────────────────────────┐           │
│  │ Network Namespace           │           │
│  │ eth0 (10.244.1.6)           │           │
│  └─────────────┬───────────────┘           │
│                │                           │
│              veth                          │
│                ║                           │
│                ↓                           │
│         Node networking                    │
└────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

The core chain to remember:

Pod → Network Namespace → eth0 → veth pair → Node networking → CNI
Enter fullscreen mode Exit fullscreen mode

And two important corollaries:

  • Each Pod gets its own IP.
  • Containers within the same Pod share that Pod's network namespace, IP, and can talk to each other via localhost.

The fundamental communication model in Kubernetes is simply:

Pod A → Pod IP → Pod B
Enter fullscreen mode Exit fullscreen mode

Level 1 Checkpoint

Before moving to Level 2, make sure these all make sense to you:

Q1. Who gives a Pod its networking?
→ The CNI (networking implementation).

Q2. Does a Pod have its own IP?
→ Yes.

Q3. Does each container in a Pod get its own IP?
→ No — containers in the same Pod share the Pod's network namespace and IP address.

Q4. What connects the Pod's network namespace to the Node?
→ Commonly, a veth pair.

Q5. Where does eth0 exist?
→ Inside the Pod's network namespace.

Q6. Why do we need CNI at all?
→ Kubernetes needs a networking implementation to create and configure Pod networking and provide connectivity between Pods.

What's Next: Pod-to-Pod Communication

In Level 2, we'll tackle the most important networking question in this series so far:

Pod A has 10.244.1.5. Pod B has 10.244.2.5. They're on different Nodes. How does a packet actually travel from A to B?

We'll trace the packet hop by hop:

Pod A → eth0 → veth → Node 1 → routing → CNI network → Node 2 → veth → Pod B
Enter fullscreen mode Exit fullscreen mode

...and compare same-node versus cross-node Pod communication in detail.


Conclusion

Pod networking is where Kubernetes and Linux networking truly meet. Every Pod gets its own network namespace and eth0 interface, connected to the Node via a veth pair, with an IP address allocated from the cluster's Pod CIDR — all orchestrated by the CNI plugin. Containers within a Pod share that networking environment entirely, which is why they can talk to each other over localhost but must be careful about port collisions.

The best way to cement these concepts is to try it yourself: spin up a Pod, run kubectl get pod -o wide, then kubectl exec in and inspect ip addr and ip route. Seeing the abstraction match real output is what makes the model stick.

Found this useful? Follow along for Level 2, where we trace a packet's full hop-by-hop journey between Pods on different Nodes. Drop a comment with your questions, and share this with someone still mixing up Pod CIDR and Node CIDR — let's keep demystifying Kubernetes networking together. 🚀

Top comments (1)

Collapse
 
kashish_kapoor_74af7504f0 profile image
Kashish Kapoor

Great source 👏
Well explained