Skip to main content
RunBook Academy

KubernetesVIII · PodsPods

Pod IP and the shared network namespace

Intermediate⏱ ~16 minkubectl

What you'll learn

  • Explain how a Pod gets its IP address and which Linux primitive holds it
  • Reason about the shared network namespace between containers in a Pod
  • Trace Pod-to-Pod traffic through the CNI and node network stack
  • Identify when hostNetwork breaks the Pod network model

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

Every Pod has one IP address. Every container in the Pod sees that IP and shares the network namespace. This lesson explains how the IP is assigned, how containers share the namespace, and how Pod-to-Pod traffic flows through the CNI.

One IP per Pod

The fundamental Kubernetes network model:

  • Every Pod gets its own IP address.
  • Pods on any node can communicate with all other Pods without NAT.
  • Agents on a node (kubelet, system processes) can communicate with all Pods on that node without NAT.
  • Pods in the same node can communicate without NAT.

This is “flat networking” — every Pod has a routable address that any other Pod can reach. The implementation:

  1. The kubelet on a node receives a Pod to schedule.
  2. The kubelet calls the CRI (containerd) to create a sandbox container (a paused container that holds the namespaces).
  3. The kubelet calls the CNI plugin to attach the sandbox’s network namespace to the node network.
  4. The CNI plugin assigns an IP from the cluster’s Pod CIDR (e.g., 10.244.0.0/16) and configures a veth pair connecting the sandbox to a bridge on the node.
  5. The kubelet then asks the CRI to start each container inside the sandbox; each container inherits the sandbox’s network namespace.
flowchart LR
    Sandbox["Sandbox<br/>(network namespace)"] --> C1["Container A<br/>(shares net ns)"]
    Sandbox --> C2["Container B<br/>(shares net ns)"]
    Sandbox --> Veth["veth pair"]
    Veth --> Bridge["cbr0 / cni0<br/>(bridge on node)"]
    Bridge --> Node["Node NIC"]
    Node --> Fabric["Cluster network"]
    Fabric --> Pod2["Other Pod"]

The sandbox is a real Linux container (running pause) that holds the namespaces and stays alive for the lifetime of the Pod. When the Pod terminates, the kubelet tears down the sandbox, the veth pair, and the IP is returned to the pool.

Containers share the network namespace

All containers in a Pod share the sandbox’s network namespace. This means:

  • They share the same IP address (the Pod IP).
  • They share the same ports (no port collisions if they bind to different ports).
  • They can communicate over localhost.
  • They see each other’s network connections in netstat, ss, etc.

If container A listens on port 8080 and container B listens on port 9090, both are accessible on the Pod’s IP at those ports. Container A can reach container B at localhost:9090. They share the loopback interface.

kubectl exec web-7c8 -c sidecar -- curl localhost:8080/healthz
# from the sidecar's perspective, port 8080 is localhost
# even though it's the main container's listener

This is the standard sidecar pattern: the sidecar uses localhost to interact with the main container’s ports, without going through the Pod IP and the CNI bridge.

How Pod-to-Pod traffic flows

sequenceDiagram
    participant App1 as Pod A<br/>container
    participant Veth1 as veth on node 1
    participant Bridge1 as cbr0 on node 1
    participant Fabric as Cluster network
    participant Bridge2 as cbr0 on node 2
    participant Veth2 as veth on node 2
    participant App2 as Pod B<br/>container

    App1->>Veth1: send packet to Pod B IP
    Veth1->>Bridge1: bridge forward
    Bridge1->>Fabric: route to node 2
    Fabric->>Bridge2: deliver to node 2
    Bridge2->>Veth2: bridge forward
    Veth2->>App2: deliver to Pod B

The flow:

  1. Container in Pod A sends a packet to Pod B’s IP.
  2. The packet exits through the veth pair (the kernel sees the packet going from the sandbox’s network namespace to the node’s bridge).
  3. The bridge forwards the packet based on the destination IP. If Pod B is on the same node, the bridge delivers to Pod B’s veth directly. If Pod B is on a different node, the bridge sends the packet up to the node’s routing table.
  4. The node’s routing table sends the packet out via the node’s NIC, across the cluster network (a routed fabric, often VXLAN or BGP), to the destination node.
  5. The destination node’s routing table receives the packet, sends it to its bridge, which delivers to Pod B’s veth.

The packet never goes through the host network stack as a host process. Containers communicate directly via the veth and bridge.

How Pod-to-Service traffic flows

Services provide a stable virtual IP for a set of Pods. The kube-proxy on each node watches the API server and configures iptables (or IPVS) rules that DNAT traffic to the Service IP to one of the backing Pod IPs.

sequenceDiagram
    participant App as Pod A<br/>container
    participant Veth as veth
    participant Bridge as cbr0
    participant IPTables as iptables<br/>DNAT to Pod B
    participant Node as Node NIC
    participant PodB as Pod B

    App->>Veth: send to Service IP
    Veth->>Bridge: bridge forward
    Bridge->>IPTables: DNAT rule matches
    IPTables->>Node: rewritten to Pod B IP
    Node->>PodB: deliver

The flow:

  1. Container sends a packet to the Service IP (e.g., 10.96.0.1).
  2. The packet exits via the veth to the bridge.
  3. The bridge hits iptables (kube-proxy’s rules); the rule matches the Service’s IP and DNATs the destination to one of the backing Pod IPs.
  4. The packet is rewritten and forwarded normally.
  5. The destination Pod receives the packet; the source sees the Service IP, not the Pod IP.

kube-proxy implements this in iptables (the default) or IPVS (a hash-table-based virtual server for higher scale). Either way, the Service IP is never assigned to any interface — it is a virtual IP that only exists in the iptables/IPVS rules.

hostNetwork breaks the model

spec:
  hostNetwork: true

Setting hostNetwork: true puts the Pod directly on the node’s network namespace:

  • The Pod IP = node IP.
  • The Pod sees all node interfaces, all node ports.
  • The Pod is reachable on the node’s IP at whatever port it binds.
  • The Pod’s ports collide with other hostNetwork Pods on the same node.

Use cases for hostNetwork:

  • Node agents (CNI plugins, log shippers) that need to bind on the node’s interfaces.
  • Pods that need to access host-local services (e.g., a node-local cache).
  • Pods that need to bind a privileged port (e.g., 80) without configuring the kubelet’s --allow-privileged or using unprivileged port mapping.

Risks:

  • Port collisions: two hostNetwork Pods on the same node cannot bind the same port.
  • Reduced scheduling: only one Pod per node per port.
  • Network policy doesn’t apply: traffic to/from hostNetwork Pods bypasses NetworkPolicy (the CNI treats them as different from regular Pods).

Production discipline: avoid hostNetwork except for node agents. For Pods that need a privileged port, run the container as non-root with securityContext.capabilities.add: [NET_BIND_SERVICE].

Cross-course references

  • The Linux course part I-Linux-Foundations covers Linux namespaces (man 7 namespaces); the Pod’s network namespace is the same primitive.
  • The Docker course part XXXI-Docker-Networking covers container networking; Pod-level networking is the cluster-level equivalent.
  • The VyOS course part XXXVII-VyOS-Firewall covers routed networking; the cluster network is similar in structure.

Quiz

Knowledge check · 4 questions

  1. Q1. What holds the network namespace for a Pod's containers?

  2. Q2. The Service ClusterIP is assigned to an interface on the node so that pods can ARP for it.

  3. Q3. A Pod has two containers: nginx (main, port 8080) and a metrics exporter sidecar that needs to scrape nginx's `/metrics` endpoint. The team wonders whether the sidecar should call `localhost:8080/metrics` or the Pod IP. Diagnose and recommend.

    Pod `web-7c8`. Container `nginx` listens on port 8080 (containerPort declared). Container `metrics-exporter` runs every 30s and curls nginx to scrape metrics. The team is debating between `localhost:8080/metrics` (same-Pod communication) and the Pod IP (`10.244.3.7:8080/metrics`)

  4. Q4. When is `hostNetwork: true` the right choice, and what is the production risk?

Passing score: 75%. Answers are checked in this browser.

Production discipline

  • Treat Pod IPs as ephemeral. Anything that needs a stable address uses a Service, not a Pod IP.
  • Use localhost for sidecar-to-main communication. Same-Pod containers share the loopback interface; this is faster and simpler than using the Pod IP.
  • Avoid hostNetwork for application Pods. Reserve it for node agents that genuinely need to bind on the host.
  • Use capabilities.add: [NET_BIND_SERVICE] for privileged ports instead of hostNetwork. Let the application bind to a low port without taking over the node’s network namespace.
  • Inspect the bridge and veth on the node during debugging. ip link show, brctl show, iptables -t nat -L reveal the cluster network state when traffic misbehaves.