Part 5 of 751 min read · 21 diagramsAI-assisted

Network Namespaces & Virtual Networking

Table of Contents#

  1. Why This Part Exists
  2. What a Linux Namespace Actually Is
  3. The Network Namespace Specifically
  4. Creating and Inspecting Network Namespaces With ip netns
  5. veth Pairs — the Virtual Cable Between Two Namespaces
  6. A Minimal Two-Namespace Setup, Built Up Step by Step
  7. Linux Bridges — a Virtual Switch
  8. Connecting Many Namespaces Through a Bridge
  9. Giving a Bridged Namespace Internet Access
  10. This Is Exactly How Container Networking Works
  11. VXLAN — Encapsulation for Cross-Host Overlay Networks
  12. VTEPs — the Endpoints That Make VXLAN Work
  13. A Worked Example: a Two-Host VXLAN Overlay
  14. Overlay vs. Underlay — the Encapsulation Tradeoff
  15. How CNI Plugins Actually Build Pod Networking
  16. Calico's Alternative — Routing Instead of Overlay
  17. Other Namespace Types — PID, Mount, UTS, IPC, User
  18. Namespaces as the Foundation of Containers
  19. ip link and ip addr — the Command Reference This Chapter Has Been Using
  20. Debugging Namespace and Virtual Networking Issues
  21. MTU and Encapsulation Overhead — a Genuine Overlay Cost
  22. Network Namespaces and This Chapter's Own netfilter Material
  23. A Full Worked Example: Building a Mini Container Network by Hand
  24. IP-in-IP — a Simpler Alternative Encapsulation
  25. Namespace Persistence — Why ip netns Namespaces Survive Without a Running Process
  26. A Kubernetes-Specific Detail — the Pause Container and Shared Pod Networking
  27. macvlan and ipvlan — Bridge Alternatives Worth Knowing
  28. Namespace Resource Overhead — How Lightweight Is "Lightweight," Really
  29. Service Meshes and This Chapter's Own Namespace Material
  30. Key Terms Glossary — This Chapter's Vocabulary in One Place
  31. DNS Resolution Inside a Namespace
  32. A Namespace Networking Decision Checklist
  33. Performance Monitoring Across Namespace Boundaries
  34. A Comparison Table: veth+Bridge vs. macvlan vs. ipvlan vs. VXLAN
  35. Cloud Provider VPC Networking — Where the Overlay-vs-Routed Decision Actually Gets Made
  36. A Firewall/Namespace Change Checklist for Production Nodes
  37. Common Mistakes
  38. Worked Practice Problems
  39. Summary and What's Next

Why This Part Exists#

Part 4 covered netfilter's packet-filtering and NAT machinery in depth, closing with a direct callback to Kubernetes and CNI plugins — but deliberately deferred one foundational question: how does a container, or a Kubernetes Pod, get its own isolated network stack — its own IP address, its own routing table, its own set of interfaces — in the first place? This chapter answers that question directly, at the level of the actual Linux kernel primitives involved, rather than treating "containers get their own network" as an unexplained given.

Diagram

Everything in this chapter is worth understanding as the literal, hands-on-buildable foundation underneath a sentence like "each Pod gets its own IP address" from this course's own Kubernetes Deep Dive series — by the end of this chapter, that sentence stops being an abstraction and becomes a specific, reproducible sequence of ip netns, ip link, and bridge commands.


What a Linux Namespace Actually Is#

A Linux namespace is a kernel feature that partitions a specific global system resource so that a set of processes sees its own, isolated instance of that resource — worth understanding this general definition before narrowing to the network namespace specifically, since Linux has several namespace types, each isolating a different resource.

Diagram

The precise mental model worth internalizing: a namespace is not a virtual machine, and it's not a separate kernel — it's the SAME kernel, with one specific global resource partitioned so a given set of processes can no longer see or affect the rest of the system's instance of that resource. This is the fundamental mechanism containers are built on (covered in depth later in this chapter) — genuinely lighter-weight than a VM's full hardware virtualization, since only the resource(s) actually being isolated are duplicated, not an entire OS/kernel instance.


The Network Namespace Specifically#

A network namespace (netns) isolates the network stack specifically — a process inside one network namespace has its own network interfaces, its own routing table, its own iptables/nftables ruleset (Part 4's own material, now scoped per-namespace), and its own set of open sockets, completely separate from every other network namespace on the same host.

Diagram

Worth stating directly why this matters practically, not just conceptually: two processes in two different network namespaces can each bind to port 8080 simultaneously with zero conflict, since "port 8080" only has meaning within a specific network namespace's own, isolated port space — the exact property that lets many containers on one host each run a web server on the same conventional port without collision. This is the single mechanism underneath "every container gets its own IP and its own ports," worth recognizing as this specific kernel feature rather than an unexplained container-runtime capability.


Creating and Inspecting Network Namespaces With ip netns#

# Create a new, named network namespace
ip netns add ns1

# List existing namespaces
ip netns list

# Run a command INSIDE a namespace
ip netns exec ns1 ip addr show

# Enter an interactive shell inside a namespace
ip netns exec ns1 bash

Worth trying this literally, since the result is worth seeing directly rather than taking on faith: ip netns exec ns1 ip addr show immediately after creation shows only a lo (loopback) interface, and nothing else — a brand-new network namespace starts genuinely empty, with no connectivity to anything, including the host's own default namespace. Every subsequent section of this chapter is, in effect, building up the plumbing needed to connect that isolated, empty namespace to something useful — a peer namespace, a bridge, or eventually the wider internet.


veth Pairs — the Virtual Cable Between Two Namespaces#

A veth pair (virtual Ethernet pair) is two virtual network interfaces, always created together, permanently linked — anything sent into one end comes out the other end, exactly like a physical Ethernet cable connecting two real network ports.

Diagram
# Create a veth pair — veth-a and veth-b are permanently linked
ip link add veth-a type veth peer name veth-b

# Move veth-b INTO the ns1 namespace created earlier
ip link set veth-b netns ns1

The "always created as a pair, always linked" property is the single detail worth internalizing about veth interfaces — you cannot create just one end; the kernel always creates both simultaneously, and traffic sent into either end always emerges from the other, regardless of which network namespace each end currently lives in. This is precisely what makes veth pairs the standard mechanism for connecting an isolated network namespace to the rest of the system: one end stays in the host's default namespace (or gets attached to a bridge, covered next), and the other end moves into the isolated namespace, becoming that namespace's own connection to the outside world.


A Minimal Two-Namespace Setup, Built Up Step by Step#

# 1. Create two namespaces
ip netns add ns1
ip netns add ns2

# 2. Create a veth pair connecting them directly
ip link add veth1 type veth peer name veth2
ip link set veth1 netns ns1
ip link set veth2 netns ns2

# 3. Assign IP addresses inside each namespace
ip netns exec ns1 ip addr add 10.0.0.1/24 dev veth1
ip netns exec ns2 ip addr add 10.0.0.2/24 dev veth2

# 4. Bring the interfaces up (they start DOWN by default)
ip netns exec ns1 ip link set veth1 up
ip netns exec ns2 ip link set veth2 up
ip netns exec ns1 ip link set lo up
ip netns exec ns2 ip link set lo up

# 5. Test connectivity
ip netns exec ns1 ping -c 3 10.0.0.2

Step 4 deserves specific emphasis, since it's the single most common cause of "I followed the steps but it doesn't work" confusion for anyone trying this hands-on for the first time: every network interface, including loopback, starts in the DOWN administrative state when created and must be explicitly brought UP — a detail easy to forget when working through a multi-step setup, and one that produces a confusing "interface exists but nothing works" symptom rather than an obvious error message. With both ends up and addressed, ns1 and ns2 can now reach each other directly over the veth pair — two genuinely isolated network namespaces, connected by exactly one virtual cable, with no host involvement in the actual packet path between them at all.


Linux Bridges — a Virtual Switch#

A single veth pair connects exactly two namespaces — for more than two, a Linux bridge acts as a virtual network switch, letting many interfaces (veth ends, physical NICs, or other virtual interfaces) all communicate as if attached to the same physical Ethernet segment.

Diagram
# Create a bridge
ip link add br0 type bridge
ip link set br0 up

# Attach a veth end to the bridge (the OTHER end lives inside a namespace)
ip link set veth1-host master br0

The "even a real physical NIC can be attached" detail is worth stating explicitly, since it resolves a common point of confusion: a Linux bridge treats every attached interface identically, whether it's a virtual veth end or the host's own real physical network card — this is exactly the mechanism that lets a bridge provide genuine internet access to the namespaces attached to it, covered in the next section, by also attaching the host's real uplink interface to the same bridge.


Connecting Many Namespaces Through a Bridge#

# For each namespace, create a veth pair — one end goes to the namespace,
# the other end attaches to the shared bridge
for i in 1 2 3; do
  ip netns add ns$i
  ip link add veth$i type veth peer name veth$i-br
  ip link set veth$i netns ns$i
  ip link set veth$i-br master br0
  ip link set veth$i-br up
  ip netns exec ns$i ip addr add 10.0.0.1$i/24 dev veth$i
  ip netns exec ns$i ip link set veth$i up
  ip netns exec ns$i ip link set lo up
done
ip addr add 10.0.0.1/24 dev br0

Reading this as the direct generalization of the previous section's two-namespace example: instead of one veth pair connecting exactly two namespaces directly to each other, each namespace now gets its own veth pair with one end attached to the shared bridge — the bridge itself acts as the switch that lets any of the three namespaces reach any other, and reach the bridge's own IP (10.0.0.1, assigned to br0 itself in the host's default namespace) as a shared gateway address. This exact topology — many namespaces, each with one veth end on a shared bridge — is, concretely, what a container runtime like Docker or containerd sets up automatically every time a new container starts, worth recognizing directly as the same pattern this chapter's own hand-built example demonstrates.


Giving a Bridged Namespace Internet Access#

The setup so far lets namespaces reach each other and the bridge itself — reaching the actual internet requires two further pieces, both direct callbacks to Part 4's own material.

Diagram
# Inside the namespace: set the bridge's IP as the default gateway
ip netns exec ns1 ip route add default via 10.0.0.1

# On the host: enable IP forwarding (off by default)
sysctl -w net.ipv4.ip_forward=1

# On the host: MASQUERADE outbound traffic from the bridge subnet (Part 4's own mechanism)
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE

This is worth reading as the direct, concrete convergence of this entire chapter with Part 4's own material: the MASQUERADE rule here is not a new concept — it's the identical mechanism Part 4 covered for letting a private subnet share one public IP, now applied to a subnet built entirely from this chapter's own namespace/bridge primitives. net.ipv4.ip_forward deserves specific mention as a genuinely easy-to-forget prerequisite — it's disabled by default on most Linux distributions (a host isn't a router unless explicitly configured to act as one), and every step in this section's setup silently fails to provide real connectivity without it, producing a confusing "the routes look correct but nothing reaches the internet" symptom.


This Is Exactly How Container Networking Works#

Worth stating directly, since it's the entire practical payoff of this chapter's hands-on sections so far: a Docker container's default "bridge" networking mode is, literally, the exact sequence of steps this chapter has just built by hand — a network namespace per container, a veth pair connecting it to a bridge (docker0, by default), and MASQUERADE/IP-forwarding rules providing outbound internet access.

Diagram

This is worth internalizing as more than a passing analogy — it is literally the same underlying mechanism, automated: docker network inspect bridge shows the real docker0 bridge this chapter's own br0 example directly parallels, and nsenter --net=/proc/<container-pid>/ns/net ip addr (or the equivalent via docker exec) reveals the exact same veth-pair-into-a-namespace topology this chapter built by hand. A platform engineer who has worked through this chapter's own hands-on sections has, in a very concrete sense, already built a miniature version of Docker's own default networking model from first principles.


VXLAN — Encapsulation for Cross-Host Overlay Networks#

Everything covered so far in this chapter connects namespaces on the same host. Real Kubernetes clusters span many nodes — Pods on different nodes need to reach each other as if they were on the same flat network, despite being separated by a real, routed physical network in between. VXLAN is one of the standard mechanisms solving exactly this problem.

Diagram

The core mechanism worth stating precisely: VXLAN encapsulates an entire Ethernet frame — not just an IP packet, the full Layer 2 frame — inside a UDP packet, which is then routed across the real underlying network exactly like any other UDP traffic. This is what makes an "overlay network" genuinely overlay: the Pods' own IP addresses (10.244.x.x, in the diagram above) exist purely within the encapsulated frames — the real, physical network in between never sees or routes based on those overlay addresses directly at all, only the outer UDP packets between the two nodes' real IPs.


VTEPs — the Endpoints That Make VXLAN Work#

A VTEP (VXLAN Tunnel Endpoint) is the component — one per participating node — that actually performs encapsulation and decapsulation, maintaining a mapping between overlay MAC/IP addresses and the real underlay IP of the node currently hosting them.

# Create a VXLAN interface on the host — this IS the VTEP
ip link add vxlan0 type vxlan id 100 dev eth0 dstport 4789

# Give it an address in the overlay network's own address space
ip addr add 10.244.1.1/24 dev vxlan0
ip link set vxlan0 up

The id 100 parameter deserves specific mention, since it's the field that actually separates one overlay network from another sharing the same physical underlay: this is the VXLAN Network Identifier (VNI), and two VXLAN interfaces with different VNI values are completely isolated from each other even while their encapsulated traffic travels over the identical physical network — the same tenant-isolation property a Kubernetes cluster's own overlay CNI plugin relies on to keep one cluster's pod-to-pod traffic logically separate from another's, if multiple overlay networks happen to share physical infrastructure.


A Worked Example: a Two-Host VXLAN Overlay#

# On Node 1 (underlay IP: 192.168.1.10)
ip link add vxlan0 type vxlan id 100 remote 192.168.1.11 dstport 4789 dev eth0
ip addr add 10.244.1.1/24 dev vxlan0
ip link set vxlan0 up

# On Node 2 (underlay IP: 192.168.1.11)
ip link add vxlan0 type vxlan id 100 remote 192.168.1.10 dstport 4789 dev eth0
ip addr add 10.244.1.2/24 dev vxlan0
ip link set vxlan0 up

# Test: from Node 1, ping Node 2's OVERLAY address
ping -c 3 10.244.1.2

Worth reading this remote parameter carefully, since it's the piece that actually establishes the tunnel: each node's VXLAN interface is configured with the OTHER node's real, underlay IP as its remote endpoint — traffic sent to the overlay address 10.244.1.2 is encapsulated and sent, as a real UDP packet, to 192.168.1.11 (Node 2's real address), where that node's own VTEP decapsulates it and delivers the original frame locally. A real Kubernetes overlay CNI plugin (Flannel's VXLAN backend, for instance) automates exactly this configuration across every node in a cluster, dynamically maintaining the correct remote mappings as nodes join and leave, rather than the static, two-node, hand-configured version shown here.


Overlay vs. Underlay — the Encapsulation Tradeoff#

Worth a direct, honest comparison, since VXLAN-style overlay networking is a genuine tradeoff, not an unconditionally better approach — this chapter's own later section on Calico presents the concrete alternative.

ApproachHow cross-node traffic worksTradeoff
Overlay (VXLAN)Encapsulated inside UDP, tunneled across the physical networkWorks on ANY underlying network topology, including ones with no control over routing — at the cost of real encapsulation overhead (covered later in this chapter's MTU section)
Underlay/routed (e.g. Calico's BGP mode)Real IP routing, no encapsulation — the physical network's own routers are configured (via BGP) to route pod IPs directlyGenuinely lower overhead and simpler packet inspection — but requires actual control over/integration with the underlying network's routing, not available in every environment

This tradeoff is worth connecting directly back to this course's own Kubernetes Deep Dive series' CNI plugin coverage: a CNI plugin's choice between an overlay mode and a routed mode is, concretely, a choice between the two rows of this table — an overlay mode (VXLAN or similar) works in genuinely any environment (a managed cloud VPC with no BGP access, for instance) at some encapsulation cost, while a routed mode needs real integration with the underlying network fabric but avoids that cost entirely, a decision worth making deliberately based on the actual constraints of the physical/cloud network a cluster is running on rather than defaulting to either option blindly.


How CNI Plugins Actually Build Pod Networking#

Worth a direct, concrete synthesis tying every mechanism covered so far in this chapter into the actual CNI plugin workflow this course's Kubernetes series has referenced without fully explaining.

Diagram

Every single step in this diagram is a mechanism this chapter has already covered directly and hands-on — there is no additional, unexplained magic in "how does a Pod get its own IP address." A CNI plugin is, concretely, a program that automates exactly this sequence (namespace creation, veth pairing, bridge/overlay attachment, IP assignment) on every Pod's creation, using the identical kernel primitives this chapter has walked through by hand — worth recognizing directly, since it means a platform engineer debugging a Kubernetes networking issue can use every tool and technique this chapter has demonstrated (ip netns exec, inspecting veth pairs, checking bridge membership) against a real cluster's real Pods, not just a chapter's own toy example.


Calico's Alternative — Routing Instead of Overlay#

Worth a concrete example of the "routed, no-overlay" alternative from this chapter's own overlay-vs-underlay comparison, directly relevant since Calico is one of the most widely deployed CNI plugins in production Kubernetes.

Diagram

Worth stating precisely what genuinely differs from the VXLAN-based approach covered earlier: Calico's default mode still uses veth pairs to connect each Pod's own namespace (the identical mechanism this chapter has covered throughout) — what's different is what happens AFTER that point, where instead of encapsulating cross-node traffic in VXLAN, Calico configures each node's own real kernel routing table and uses BGP to propagate routes between nodes, so cross-node Pod traffic is routed as plain, unencapsulated IP traffic. This avoids VXLAN's encapsulation overhead entirely, at the cost of requiring a network environment where this kind of dynamic, BGP-based routing is actually viable — some managed cloud VPCs restrict this, which is exactly why Calico (and most CNI plugins generally) also offer a VXLAN/overlay fallback mode for environments where routed mode isn't an option.


Other Namespace Types — PID, Mount, UTS, IPC, User#

This chapter has focused specifically on the network namespace — worth a brief, honest survey of the other namespace types Linux provides, since a real container isolates far more than just its network stack.

Namespace typeWhat it isolates
netNetwork interfaces, routing tables, iptables/nftables rules — this chapter's entire focus
pidProcess IDs — a process inside a PID namespace sees itself as PID 1, unaware of processes outside
cgroupIsolates the CGROUP hierarchy view itself — a further, distinct namespace type from cgroups' own resource-limiting mechanism
mnt (mount)The filesystem mount table — lets a container see a completely different root filesystem
utsHostname and domain name — lets each container have its own hostname
ipcInter-process communication resources (shared memory, semaphores)
userUID/GID mappings — lets a process be "root" inside the namespace while mapping to an unprivileged UID on the host

The user namespace deserves specific security-relevant emphasis: it's the mechanism that lets a container run as "root" from its own internal perspective (UID 0 inside the namespace) while that same process is actually mapped to a genuinely unprivileged, non-root UID on the host system — a real, meaningful security boundary reducing the blast radius of a container escape, since a compromised "root" process inside a user-namespaced container doesn't automatically have real root privileges on the underlying host. This directly connects to the DevSecOps series' own container-hardening material — rootless container runtimes (Podman's default mode, for instance) rely specifically on user namespaces to achieve genuine root-free container execution.


Namespaces as the Foundation of Containers#

Worth closing this chapter's conceptual arc explicitly: a "container," from the Linux kernel's own point of view, is not a distinct kernel object at all — it's simply a process (or group of processes) running inside a specific combination of namespaces (typically net, pid, mnt, uts, ipc, and often user), usually also constrained by cgroups (resource limits — a genuinely separate kernel mechanism this series' systemd chapter touches on) for CPU/memory limiting.

Diagram

This is worth stating as the single most important conceptual payoff of this entire chapter: "container" is a userspace concept (implemented by tools like Docker, containerd, or Podman), not a kernel-level one — the kernel only knows about namespaces and cgroups, general-purpose primitives that happen to compose into what userspace tooling then presents to a user as "a container." docker run, under the hood, is simply a program that creates the right combination of namespaces (this chapter's own net namespace among them), sets up cgroup limits, and then executes a process inside that combination — nothing more mysterious than that, and every mechanism this chapter has demonstrated by hand contributes directly to that combination.


Worth a consolidated, quick-reference summary of the ip subcommands this chapter has used throughout, gathered in one place rather than scattered across many worked examples.

CommandPurpose
ip link add <name> type veth peer name <name2>Create a veth pair
ip link set <iface> netns <namespace>Move an interface into a different network namespace
ip link set <iface> master <bridge>Attach an interface to a bridge
ip link set <iface> up / downBring an interface administratively up or down
ip addr add <cidr> dev <iface>Assign an IP address to an interface
ip netns exec <namespace> <command>Run a command inside a specific network namespace
ip route add default via <gateway>Set a default route

Worth noticing this table as, in effect, a summary of nearly this entire chapter's own hands-on material — every worked example above has been composed entirely from this small set of commands, applied in different combinations and orders. This is deliberately worth internalizing: real container networking, real CNI plugin behavior, and real Kubernetes Pod networking all reduce to exactly this same small vocabulary, applied programmatically rather than by hand — there is no larger, separate command surface a platform engineer needs to learn beyond what this table already contains.


Debugging Namespace and Virtual Networking Issues#

# List every network namespace on the host
ip netns list

# See what's inside a specific namespace
ip netns exec ns1 ip addr show
ip netns exec ns1 ip route show

# Find which namespace a given PID belongs to
ls -la /proc/<pid>/ns/net

# Enter a running container's network namespace directly (no docker exec needed)
nsenter --net=/proc/<container-pid>/ns/net ip addr show

# Check bridge membership
bridge link show

The nsenter technique deserves specific emphasis as a genuinely powerful, tool-agnostic debugging capability: it lets a platform engineer inspect ANY process's network namespace directly via the kernel's own /proc/<pid>/ns/net handle, without depending on docker exec, kubectl exec, or any container-runtime-specific tooling being available or functional — a real advantage during an incident where the container runtime itself might be part of the problem, since nsenter only depends on having a PID and access to /proc, working identically whether the process in question is a Docker container, a Kubernetes Pod, or a namespace this chapter built entirely by hand.


MTU and Encapsulation Overhead — a Genuine Overlay Cost#

Worth a direct, quantified return to the overlay-vs-underlay tradeoff introduced earlier in this chapter: VXLAN's encapsulation isn't free, and the specific cost is worth naming precisely rather than left as a vague "some overhead."

Diagram

The concrete, practical fix worth stating directly: an overlay network's own interfaces (this chapter's vxlan0, in the earlier worked example) need their MTU explicitly reduced to account for the encapsulation overhead — typically 1450 bytes instead of the standard 1500, leaving exactly enough headroom for VXLAN's own ~50 bytes of added header. A cluster running an overlay CNI plugin with a mismatched MTU configuration is a genuinely common, hard-to-diagnose source of intermittent connectivity issues — small packets work fine, but any traffic approaching the standard 1500-byte MTU (a large HTTP response body, for instance) mysteriously fails or performs poorly, precisely because it silently exceeds the overlay's own effective MTU once encapsulation overhead is accounted for.


Network Namespaces and This Chapter's Own netfilter Material#

Worth a direct callback to Part 4, closing the loop between these two chapters explicitly: every network namespace has its own, completely independent netfilter/iptables/nftables state — a firewall rule applied in the host's default namespace has zero effect on traffic inside a separate namespace, and vice versa.

# A firewall rule in the HOST's default namespace...
iptables -A INPUT -p tcp --dport 8080 -j DROP

# ...has ZERO effect inside a different namespace:
ip netns exec ns1 iptables -L INPUT
# (shows an EMPTY, independent ruleset — completely unaffected by the host's own rule above)

This is worth stating precisely, since it directly explains a genuinely common point of confusion for anyone debugging container networking with Part 4's own tools: a firewall rule applied on the host does NOT automatically apply inside a container's own network namespace, because that namespace has its own, entirely separate netfilter state. This is exactly why a Kubernetes NetworkPolicy-enforcing CNI plugin (per Part 4's own CNI-plugin callback) applies its generated iptables/nftables rules specifically inside (or targeting) each Pod's own network namespace, not just at the host level — the enforcement point has to match where the actual namespace boundary this chapter has covered throughout actually lives.


A Full Worked Example: Building a Mini Container Network by Hand#

Tying every mechanism from this chapter into one complete, runnable sequence — three isolated namespaces, bridged together, with internet access, and namespace-scoped firewall rules, entirely from first principles.

#!/bin/bash
set -e

# 1. Bridge, acting as the virtual switch
ip link add br0 type bridge
ip addr add 10.0.0.1/24 dev br0
ip link set br0 up

# 2. Three namespaces, each with its own veth pair into the bridge
for i in 1 2 3; do
  ip netns add ns$i
  ip link add veth$i type veth peer name veth$i-br
  ip link set veth$i netns ns$i
  ip link set veth$i-br master br0
  ip link set veth$i-br up
  ip netns exec ns$i ip addr add 10.0.0.1$i/24 dev veth$i
  ip netns exec ns$i ip link set veth$i up
  ip netns exec ns$i ip link set lo up
  ip netns exec ns$i ip route add default via 10.0.0.1
done

# 3. Host-side: forwarding + MASQUERADE for internet access (Part 4's own material)
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE

# 4. A NAMESPACE-SCOPED firewall rule, applying ONLY to ns2 (this chapter's own material)
ip netns exec ns2 iptables -A INPUT -p tcp --dport 22 -j DROP

echo "ns1 -> ns3 connectivity:"
ip netns exec ns1 ping -c 2 10.0.0.13

Worth reading this script as the concrete, cumulative synthesis of this entire chapter: bridges, veth pairs, namespace-scoped IP addressing, host-level MASQUERADE (a direct callback to Part 4), and a namespace-scoped firewall rule (also Part 4's own mechanism, now demonstrated as genuinely per-namespace) all composed together into one working, three-node virtual network — built entirely from primitives a bare Linux kernel already provides, with no container runtime involved at all. This is, deliberately, a smaller, hand-built, fully-understood version of exactly what Docker, containerd, and every CNI plugin covered across this chapter automate at production scale.


IP-in-IP — a Simpler Alternative Encapsulation#

Worth a direct comparison to VXLAN, since it's not the only overlay encapsulation mechanism in production use — IP-in-IP (also called IPIP) is a simpler, lower-overhead alternative worth knowing by name, since it's the default overlay mode for several real CNI plugins including Calico's own overlay fallback.

Diagram

The concrete tradeoff worth stating precisely: VXLAN preserves the full Ethernet frame (including MAC addresses), which matters if anything in the overlay network genuinely depends on Layer 2 semantics (certain multicast/broadcast-dependent protocols); IP-in-IP only encapsulates the IP packet itself, which is sufficient — and meaningfully lower-overhead — for the very common case where an overlay network only needs to provide IP-layer (Layer 3) connectivity between Pods, which is nearly always the actual requirement in a Kubernetes context. This is exactly why Calico defaults to IP-in-IP for its own overlay fallback mode rather than VXLAN specifically — the lower per-packet overhead is a genuine, measurable performance win when the fuller Layer 2 semantics VXLAN preserves aren't actually needed by anything in the cluster.


Namespace Persistence — Why ip netns Namespaces Survive Without a Running Process#

Worth a subtle but important clarification: unlike most other namespace types, a network namespace created via ip netns add persists even with no process currently running inside it — worth understanding the actual mechanism, since it explains a real, sometimes-confusing operational detail.

Diagram

This is worth contrasting directly with a container's own network namespace, which typically has no such persistent file — a container's network namespace is normally kept alive only by its own running process (or, in some runtimes, by an explicit "pause" container holding the namespace open), and disappears automatically once that process exits. The ip netns tooling's explicit, file-backed persistence (that /var/run/netns/ bind mount) is specifically why this chapter's own worked examples can be built up, torn down, and inspected step by step across multiple separate commands — ip netns delete ns1 is the explicit cleanup step removing that persistent reference, worth remembering as the actual teardown counterpart to every ip netns add this chapter has demonstrated.


A Kubernetes-Specific Detail — the Pause Container and Shared Pod Networking#

Worth a direct, concrete callback to this course's own Kubernetes Deep Dive series, resolving a detail that series left unexplained: every Pod, not just every container, gets exactly one network namespace — every container within the same Pod shares that single namespace, and the mechanism making this possible is directly explainable using this chapter's own material.

Diagram

This is the concrete mechanism worth stating precisely, directly resolving "why can two containers in the same Pod both reach localhost:8080 and mean the same thing," a detail this course's Kubernetes series referenced without fully explaining: every container in a Pod is started with its network namespace explicitly set to JOIN the pause container's already-existing namespace, rather than creating a new one of its own — the exact same "join an existing namespace rather than create a new one" operation this chapter's own ip netns exec commands have been performing throughout, just automated by the container runtime and kubelet together. This is also why killing and restarting an application container within a Pod doesn't change that Pod's IP address at all — the network namespace itself (held open by the separate, rarely-restarted pause container) is what actually owns the IP, completely independent of any individual application container's own lifecycle.


macvlan and ipvlan — Bridge Alternatives Worth Knowing#

Worth a brief, honest survey of two further virtual interface types beyond veth-plus-bridge, since real production networking occasionally reaches for one of them specifically for reasons this section makes concrete.

Diagram

The concrete, practical reason to reach for macvlan specifically, worth stating directly: it lets a namespace's interface appear on the physical network as a genuinely distinct host, with its own MAC address directly reachable from other physical hosts on the same network segment — no bridge, no NAT, and no encapsulation involved at all, unlike every other mechanism this chapter has covered. This is a real, if narrower, fit for workloads that specifically need to appear as a first-class citizen on the physical LAN (certain legacy applications expecting a real, directly-addressable network presence) rather than sitting behind NAT or an overlay. ipvlan's own narrower niche — sharing one MAC address across many namespaces — matters specifically in environments where a switch port enforces a hard limit on the number of distinct MAC addresses it will learn, a genuine constraint on some managed/cloud network fabrics that macvlan's own one-MAC-per-namespace model would otherwise violate.


Namespace Resource Overhead — How Lightweight Is "Lightweight," Really#

Worth a direct, honest quantification of a claim this chapter has made repeatedly in passing — that namespaces are "genuinely lighter-weight than a VM." Worth stating precisely what that actually means in practice rather than leaving it as an unquantified assertion.

Diagram

The concrete, practical consequence of this real difference worth stating directly: a single host can support hundreds or thousands of network namespaces (and therefore, potentially, hundreds or thousands of containers) with genuinely low aggregate overhead, where the equivalent VM-based density would require dramatically more memory and CPU purely for redundant, per-instance kernel overhead. This is the actual, concrete economic reason container density on a single host so dramatically exceeds VM density on equivalent hardware — not a marketing claim, but a direct, measurable consequence of namespaces being kernel data structures rather than separate kernel instances, worth connecting back to this course's own infrastructure-cost material (the CI/CD & GitOps series' self-hosted runner chapter, in particular) whenever a cost comparison between container-based and VM-based infrastructure comes up.


Service Meshes and This Chapter's Own Namespace Material#

Worth a direct callback to this course's own Kubernetes Deep Dive series' service mesh chapter, resolving a mechanism that chapter referenced but didn't fully explain at the kernel level: a sidecar proxy's traffic interception is implemented using exactly this chapter's own namespace and netfilter primitives, applied within a single Pod's own shared network namespace.

Diagram

This is worth stating as the direct, concrete convergence of three separate pieces of material across this course: the shared-Pod-namespace mechanism from earlier in this chapter, Part 4's own iptables/nftables REDIRECT-style rules, and the service mesh sidecar pattern this course's Kubernetes series introduced without full explanation. A sidecar injection process (Istio's own injector, for instance) adds an iptables rule — applied within the Pod's own shared network namespace, using exactly the namespace-scoped filtering already demonstrated earlier in this chapter — that transparently redirects the application container's own outbound traffic to the sidecar's proxy port, before that traffic ever leaves the Pod. The application container itself requires zero code changes to be "meshed," precisely because this redirection happens at the kernel's own netfilter layer, invisible to the application process, using mechanisms this chapter and Part 4 have both covered directly and hands-on.


Key Terms Glossary — This Chapter's Vocabulary in One Place#

TermMeaning in this chapter's context
Network namespace (netns)An isolated instance of the network stack — interfaces, routing table, netfilter state
veth pairTwo permanently-linked virtual Ethernet interfaces — a virtual cable between namespaces
Linux bridgeA virtual switch, letting many interfaces (veth ends or real NICs) communicate as one segment
VXLANEncapsulates an entire Ethernet frame inside UDP — the standard overlay-network mechanism
VTEPVXLAN Tunnel Endpoint — the component performing encapsulation/decapsulation per node
VNIVXLAN Network Identifier — separates multiple overlay networks sharing one physical underlay
IP-in-IPA simpler, lower-overhead overlay encapsulating only the IP packet, not the full Ethernet frame
Overlay vs. underlay/routedEncapsulated tunneling (works anywhere) vs. real routing integration (lower overhead, needs network support)
macvlan / ipvlanAlternatives to bridge+veth giving a namespace direct physical-network presence
Pause containerThe hidden container holding a Kubernetes Pod's shared network namespace open
nsenterEnters any process's namespace directly via /proc, independent of container-runtime tooling
ip netnsThe command-line tool for creating, listing, entering, and deleting network namespaces
cgroupsThe resource-limiting kernel mechanism that, combined with namespaces, forms "a container"
macvlan / ipvlanAlternatives giving a namespace a directly-addressable presence on the physical LAN
VPC CNI (AWS)A native, cloud-integrated CNI assigning real routable VPC IPs directly to Pods, no overlay needed
/etc/netns/<name>/resolv.confThe per-namespace DNS configuration file ip netns exec bind-mounts automatically

DNS Resolution Inside a Namespace#

Worth a direct, practical extension of Part 2's own DNS material into this chapter's namespace context: a network namespace has its own, independent DNS resolver configuration, worth understanding since it's a genuinely common source of "why can't this container resolve hostnames" confusion.

Diagram
# A namespace with no DNS configuration fails hostname resolution entirely
ip netns exec ns1 curl https://example.com
# curl: (6) Could not resolve host: example.com

# Fix: give the namespace its own resolv.conf
mkdir -p /etc/netns/ns1
echo "nameserver 8.8.8.8" > /etc/netns/ns1/resolv.conf

The /etc/netns/<namespace>/resolv.conf convention deserves specific mention as the standard, ip netns-aware mechanism for this exact problem: ip netns exec automatically bind-mounts this per-namespace file over /etc/resolv.conf inside the target namespace, if it exists — meaning the fix is simply creating this file rather than needing any manual bind-mount scripting. This directly explains why a container's own DNS resolution (a Docker container gets a working /etc/resolv.conf automatically, typically pointing at Docker's own embedded DNS server or the host's configuration) works out of the box, while this chapter's own hand-built namespaces — deliberately built from bare primitives with nothing automated — require this DNS configuration step explicitly, exactly as they've required every other piece of configuration this chapter has walked through by hand.


A Namespace Networking Decision Checklist#

Worth closing this chapter's practical guidance with a single, walkable decision checklist consolidating the choices this chapter has covered — the sequence of real decisions a platform engineer actually makes when designing virtual networking for a new environment.

DecisionChoose based on
Bridge+veth vs. macvlan/ipvlanDoes the workload need to appear as a distinct host on the physical LAN? If not, bridge+veth (this chapter's own default model) is the standard, simpler choice
Overlay (VXLAN/IP-in-IP) vs. routed (BGP)Does the underlying network environment support BGP-based route propagation? If yes, routed mode avoids encapsulation overhead entirely
VXLAN vs. IP-in-IP, if overlay is chosenDoes anything genuinely depend on Layer 2 (MAC-level) semantics? If not, IP-in-IP's lower overhead is usually the better default
MTU configurationAny overlay in use — VXLAN or IP-in-IP — needs the overlay interface's MTU explicitly reduced to account for encapsulation overhead
DNS resolutionA hand-built namespace needs explicit /etc/netns/<name>/resolv.conf configuration; a container runtime typically handles this automatically

This checklist is deliberately structured as a sequence of the same real, concrete tradeoffs this chapter has covered in depth across its own sections — worth treating as a navigation aid back into the chapter's own content, and as the actual decision sequence underneath what a CNI plugin's own configuration options (VXLAN mode, BGP mode, MTU settings) represent when configuring a real Kubernetes cluster's networking.


Performance Monitoring Across Namespace Boundaries#

Worth a closing observability connection, directly extending this course's own Observability series into the namespace/virtual-networking layer specifically: standard host-level network monitoring tools, run without namespace awareness, silently miss traffic happening entirely within namespace-to-namespace or namespace-to-bridge paths.

Diagram

This is worth stating precisely, since it's a genuinely common blind spot for a team that has only ever monitored traffic at a host's real physical interface: two Pods on the same node, communicating via the same bridge (this chapter's own Kubernetes-networking material), generate traffic that a plain tcpdump -i eth0 on the host will never see at all, since that traffic never actually traverses the host's real physical interface — it stays entirely within the bridge. The fix, directly reusing this chapter's own tooling, is capturing either inside the specific namespace of interest (ip netns exec <ns> tcpdump -i <iface>) or on the bridge interface itself (tcpdump -i br0), both of which see traffic a physical-interface-only capture misses entirely — worth knowing as a standing debugging technique, not just a one-off trick, for any node-local, intra-bridge Kubernetes networking issue that doesn't reproduce when captured at the wrong layer.


A Comparison Table: veth+Bridge vs. macvlan vs. ipvlan vs. VXLAN#

Worth closing this chapter's technical survey with one final, consolidated reference table gathering every virtual networking mechanism covered — the practical "which one, when" summary.

MechanismCross-host?OverheadBest fit
veth + bridgeNo (single host only)NoneStandard, default container networking on one host
macvlanRequires physical L2 adjacencyNoneA namespace needs its own real, physically-addressable presence
ipvlanRequires physical L2 adjacencyNoneSame as macvlan, but under a switch's MAC-address-count limit
AWS VPC CNIYes (within AWS)NoneAWS-specific — real routable VPC IPs assigned directly via ENI secondary IPs
VXLANYes~50 bytes/packetCross-host overlay when Layer 2 semantics matter, or routed mode isn't viable
IP-in-IPYes~20 bytes/packetCross-host overlay when only Layer 3 connectivity is genuinely needed
Routed/BGP (no encapsulation)YesNoneCross-host, when the network environment supports BGP route propagation
AWS VPC CNI (ENI-based)Yes (within AWS only)NoneAWS-specific — real routable VPC IPs assigned directly, no overlay or BGP needed

Reading this table as this chapter's own closing decision guide: veth+bridge is the correct default for anything staying within one host (exactly what this chapter's own hands-on examples have built throughout); crossing hosts requires either accepting encapsulation overhead (VXLAN or the lower-overhead IP-in-IP) or securing genuine routing integration with the underlying network (BGP-based routed mode, avoiding encapsulation entirely). Every CNI plugin's own configuration options map directly onto a row in this table — worth reading a CNI plugin's own documentation with this table in mind, rather than treating its configuration options as an unfamiliar, plugin-specific vocabulary.

A useful closing exercise for internalizing this table: take any real CNI plugin's own installation manifest (Flannel's, Calico's, or Cilium's) and identify, from its own configuration fields alone, which row of this table it's actually implementing by default — the vocabulary differs slightly between plugins, but every one of them is, underneath its own naming choices, selecting from exactly this same small set of mechanisms this chapter has covered directly.


Cloud Provider VPC Networking — Where the Overlay-vs-Routed Decision Actually Gets Made#

Worth a closing, practical connection to real production Kubernetes clusters running on a managed cloud provider, directly extending this chapter's own overlay-vs-routed comparison: the actual decision is very often made FOR a platform team by the cloud provider's own VPC networking constraints, not freely chosen.

Diagram

This is worth stating directly as the honest, practical resolution of this chapter's own overlay-vs-routed tradeoff for a reader about to make this decision for a real cluster: on many managed cloud Kubernetes offerings, a genuinely BGP-integrated routed mode isn't available at all without special networking configuration the cloud provider may not support, making an overlay CNI mode the practical default rather than a deliberate performance tradeoff. AWS's own VPC CNI is a notable, genuine exception worth knowing by name — it assigns real, routable VPC IP addresses directly to Pods (via ENI secondary IPs), avoiding the need for either an overlay OR BGP-based routing, since the cloud's own native VPC routing already handles Pod-to-Pod traffic directly. This is worth checking explicitly against a specific cloud provider's own CNI documentation before assuming either overlay or routed mode is freely available — the actual menu of options is provider-specific, not a universal choice this chapter's own comparison table alone determines.


A Firewall/Namespace Change Checklist for Production Nodes#

Worth one final, practical checklist, directly extending Part 4's own change-management checklist to specifically cover changes touching namespace/virtual-networking configuration on a live production node.

StepWhy
1. Confirm the target namespace exists and is the intended oneip netns list — namespace names collide easily across a busy host
2. Snapshot current state before changing anythingip netns exec <ns> ip addr show / ip route show, saved for comparison
3. Apply the change
4. Verify from BOTH sides of any new veth/bridge attachmentA one-sided check can miss an asymmetric misconfiguration
5. Confirm DNS still resolves inside the namespaceA common, easy-to-overlook casualty of a networking change
6. Check MTU on any overlay interface touchedSilent fragmentation is easy to miss without an explicit check
7. Confirm the change from a genuinely independent monitoring vantage pointA physical-interface-only capture can miss intra-bridge traffic entirely, per this chapter's own monitoring section
8. Document which row of this chapter's own mechanism-comparison table the change actually implementsKeeps the design decision traceable for the next engineer, rather than an unexplained configuration choice

This checklist deliberately mirrors Part 4's own closing change-management discipline, applied specifically to this chapter's own namespace and virtual-networking material — worth treating both checklists as companions, not separate processes, for any production change touching the full stack this pair of chapters has covered.

Worth one final forward-reference before this chapter's own closing summary: every ip link/ip addr command demonstrated throughout this chapter configures interface state that does NOT automatically persist across a reboot — Part 6's own coverage of systemd-networkd picks up exactly this thread, covering how a real production host declares this same interface configuration persistently, rather than requiring it to be re-applied by hand (or by a boot-time script) every time the system starts.

A container runtime sidesteps this specific concern entirely, worth noting explicitly: it recreates each container's own namespace and veth pair fresh on every start, meaning the "does this configuration survive a reboot" question this chapter's own hand-built examples raise simply doesn't arise the same way for container networking specifically — it only matters for a host's own persistent, standing network configuration, exactly the boundary Part 6 picks up next.

Every command this chapter has demonstrated is worth keeping as a personal reference — running them by hand, on a disposable VM or container, remains the single fastest way to build real, lasting intuition for how Linux networking actually works underneath every higher-level abstraction this course covers, from a plain Docker container all the way up to a full Kubernetes cluster's own Pod networking.


Common Mistakes#

MistakeWhy it's a problemFix
Forgetting to bring a newly-created interface upEvery interface, including loopback inside a new namespace, starts administratively DOWNExplicitly ip link set <iface> up for every interface, including lo inside each namespace
Assuming a hand-built namespace has working DNS resolution automaticallyUnlike a container runtime, ip netns provides no automatic DNS configurationCreate /etc/netns/<name>/resolv.conf explicitly, per this chapter's own DNS section
Capturing traffic only on the host's physical interface when debugging intra-node Pod communicationBridge-local, namespace-to-namespace traffic never touches the physical interface at allCapture inside the specific namespace or on the bridge interface directly
Forgetting net.ipv4.ip_forward=1 on the hostThe host silently fails to route traffic between namespaces/bridge and the outside worldEnable IP forwarding explicitly — it's disabled by default on most distros
Assuming a host-level firewall rule applies inside a namespaceEvery network namespace has its own, completely independent netfilter stateApply namespace-scoped rules via ip netns exec <ns> iptables ... when the rule needs to apply there specifically
Running a VXLAN overlay with the default 1500-byte MTU unchangedEncapsulation overhead (~50 bytes) pushes packets over the underlay's real MTU, causing fragmentation or silent dropsReduce the overlay interface's MTU (typically to 1450) to account for encapsulation overhead
Choosing an overlay (VXLAN) network in an environment that could support routed/BGP mode insteadPays real, avoidable encapsulation overhead when routed mode was actually viableEvaluate whether the underlying network environment supports BGP-based routing (like Calico's routed mode) before defaulting to overlay
Assuming every cloud provider freely offers both overlay and routed CNI modesMany managed VPCs restrict dynamic BGP advertisement, making overlay the practical default regardless of preferenceCheck the specific cloud provider's own CNI documentation (e.g. AWS VPC CNI's native routing) before assuming either mode is freely available
Treating macvlan/ipvlan as a drop-in replacement for bridge+veth in every scenarioBoth require genuine physical Layer 2 adjacency, unavailable on many cloud/virtualized network fabricsReserve macvlan/ipvlan for the specific, narrower cases described in this chapter's own dedicated section
Debugging container networking without using nsenter/ip netns exec directlyRelying purely on docker exec/kubectl exec misses issues where the runtime itself is part of the problemUse nsenter --net=/proc/<pid>/ns/net to inspect a namespace directly via the kernel, independent of runtime tooling
Treating "container" as a distinct kernel conceptLeads to confusion when debugging — there's no single "container" object to inspect at the kernel levelRecognize a container as a process running inside a specific combination of namespaces (+ cgroups), and debug each primitive directly

Worked Practice Problems#

Problem 1: A platform engineer creates a new network namespace, assigns it an IP address on a veth interface, but ping from inside the namespace fails immediately with "Network is unreachable." What's the most likely cause?

Answer: The interface (and/or the namespace's loopback interface) was never brought administratively up — every interface, including a brand-new veth end and loopback inside a newly created namespace, starts in the DOWN state by default. The fix is explicitly running ip netns exec <ns> ip link set <iface> up (and the same for lo) before expecting any traffic to flow.

Problem 2: Three namespaces are correctly bridged together and can reach each other, but none of them can reach the public internet, despite a MASQUERADE rule being correctly configured on the host. What's the most likely missing piece?

Answer: net.ipv4.ip_forward is still set to 0 (disabled), which is the default on most Linux distributions unless explicitly enabled. Without IP forwarding enabled, the host will not route traffic between the bridge/namespace subnet and its own external interface at all, regardless of how correctly the MASQUERADE NAT rule itself is configured — both pieces (forwarding enabled AND the NAT rule) are required together.

Problem 3: A team applies what they believe is a restrictive iptables rule on the host, blocking inbound traffic on port 8080, but a container's own service on port 8080 remains fully reachable. Why, given this chapter's own material?

Answer: Every network namespace has its own, completely independent netfilter/iptables state — a rule applied in the host's default namespace has no effect on traffic that never actually enters that namespace's own filtering path in the way the engineer assumed (or, more precisely, the container's own namespace has its own separate ruleset that was never touched by this rule). The fix is applying the rule specifically within the container's own network namespace (nsenter/ip netns exec against that specific namespace) if the intent is to filter traffic inside it, or ensuring the rule targets the correct chain/interface if the intent was to filter traffic at the bridge/host boundary instead.

Problem 4: A Kubernetes cluster running a VXLAN-based overlay CNI plugin experiences intermittent failures specifically for larger HTTP responses, while small requests work fine. What's the most likely root cause, and what's the fix?

Answer: An MTU mismatch — the VXLAN overlay interfaces are still using the standard 1500-byte MTU, and VXLAN's own encapsulation overhead (~50 bytes) pushes any packet that was already near the standard MTU over the underlay network's real limit, causing fragmentation or silent drops specifically for larger payloads while smaller packets remain unaffected. The fix is explicitly reducing the overlay interface's MTU (typically to 1450) to leave headroom for the encapsulation overhead.

Problem 5: A platform engineer needs to debug a running container's network configuration during an incident where the container runtime's own CLI (docker) is unresponsive. What technique from this chapter lets them inspect the container's networking directly regardless?

Answer: nsenter --net=/proc/<container-pid>/ns/net, using the container's actual host-visible PID (findable via ps even when docker itself is unresponsive) to enter its network namespace directly through the kernel's own /proc interface — this technique depends only on /proc access and the PID, not on the container runtime's own CLI or daemon being functional, making it the correct fallback exactly when the runtime tooling itself is part of the problem.

Problem 6: A team restarts a crashed application container within a Kubernetes Pod, and is relieved to see the Pod's IP address remains completely unchanged afterward. Explain why, using this chapter's own material.

Answer: A Pod's IP address is owned by its shared network namespace, which is itself held open by a separate, rarely-restarted "pause" container — every application container within the Pod joins that already-existing namespace rather than creating its own. Restarting one application container has no effect on the namespace itself (and therefore no effect on the Pod's IP), since the namespace's lifecycle is tied to the pause container, not to any individual application container running within it.

Problem 7: A platform team is choosing between macvlan and a standard bridge+veth setup for a workload that specifically needs to appear as a distinct, directly-addressable host on the physical LAN, with its own real MAC address visible to other physical hosts. Which is the correct choice, and why does the standard bridge model fall short here?

Answer: macvlan — it gives the namespace's interface its own distinct MAC address directly on the physical network, appearing as a genuinely separate host to switches and other devices on the same segment, with no bridge, NAT, or encapsulation involved. The standard bridge+veth model (this chapter's own default throughout) keeps each namespace behind the bridge/host's own network presence — namespaces are reachable via the bridge and (if configured) MASQUERADE/NAT, but don't appear as independently addressable hosts directly on the physical LAN the way macvlan specifically provides.

Problem 8: A team migrating a CNI plugin from VXLAN to Calico's routed (BGP) mode asks whether they'll need to change anything about how individual Pods get their own network namespace and veth pair. What's the accurate answer, per this chapter's own material?

Answer: No — the namespace-creation and veth-pairing steps (this chapter's own foundational mechanism, used by every CNI plugin regardless of cross-node strategy) remain unchanged. What changes is specifically what happens AFTER that point: instead of encapsulating cross-node traffic in VXLAN, routed mode configures the node's own kernel routing table and uses BGP to propagate routes between nodes, avoiding encapsulation entirely for cross-node traffic. The Pod-level namespace/veth mechanics are identical between the two approaches — only the cross-node transport strategy differs.

Problem 9: A team running a self-managed Kubernetes cluster on bare-metal infrastructure asks whether AWS's VPC CNI approach (assigning real routable IPs directly to Pods) is available to them. What's the accurate answer, and why?

Answer: No — AWS's VPC CNI specifically relies on AWS's own VPC infrastructure and EC2 ENI (Elastic Network Interface) secondary IP assignment, a cloud-provider-specific integration with no bare-metal equivalent. A bare-metal cluster has no equivalent cloud-managed VPC routing fabric to integrate with this way, meaning it must choose between this chapter's own overlay options (VXLAN, IP-in-IP) or a genuinely BGP-integrated routed mode requiring real cooperation from the physical network's own routers — the same overlay-vs-routed decision this chapter has covered throughout, without AWS's own specific native-VPC-routing shortcut being an option at all outside AWS itself.

Problem 10: A platform engineer is asked to explain, without using the word "container" at all, exactly what a running Docker container actually is from the Linux kernel's own point of view. Using only this chapter's own vocabulary, how would they answer?

Answer: A running process (or group of processes) executing inside a specific combination of kernel namespaces — typically its own network namespace (giving it its own interfaces, routing table, and netfilter state), its own PID namespace (seeing itself as PID 1), its own mount namespace (its own filesystem view), and usually its own UTS and IPC namespaces — additionally constrained by cgroups for CPU/memory resource limits. There is no separate "container" object at the kernel level at all; every property a container appears to have is a direct, explainable consequence of this specific combination of general-purpose kernel primitives, every one of which this chapter has demonstrated hands-on.


Summary and What's Next#

A Linux network namespace is a kernel feature isolating the network stack (interfaces, routing table, netfilter state) for a set of processes — the same underlying kernel, with one specific global resource partitioned rather than a separate kernel or VM. veth pairs are the virtual cable connecting an isolated namespace to the rest of the system, always created in linked pairs; a Linux bridge acts as a virtual switch letting many veth ends (and even real physical NICs) communicate as one Ethernet segment, with MASQUERADE and IP forwarding (both direct callbacks to Part 4's own material) providing that bridged topology with real internet access. This exact combination — namespace, veth pair, bridge attachment — is literally what Docker's default networking mode, and every CNI plugin's Pod-networking implementation, automates on every container or Pod creation; there is no additional unexplained mechanism beyond what this chapter built by hand. VXLAN extends this same model across multiple physical hosts by encapsulating entire Ethernet frames inside UDP packets between VTEPs, at a real, quantifiable MTU cost worth explicitly accounting for — the overlay-vs-routed (Calico's BGP-based alternative) tradeoff is a genuine architectural decision, not a settled question, driven by whether a given network environment actually supports the routing integration a no-encapsulation approach requires. Beyond the network namespace specifically, Linux provides PID, mount, UTS, IPC, and user namespaces, together forming the actual, complete kernel-level definition of "a container" — a process running inside a specific combination of these namespaces plus cgroup resource limits, with no separate "container" object existing at the kernel level at all.

Part 6, immediately following, shifts to a different but related foundation: systemd — the init system and service manager responsible for starting, supervising, and (via systemd-networkd, where relevant) configuring the very network interfaces this chapter has spent its length manipulating directly, on a real, running production host.

Every hands-on example in this chapter has been runnable on a genuinely bare Linux host with no container runtime installed at all — worth remembering that fact specifically, since it's the clearest possible demonstration that container networking is not special-cased kernel behavior, but an ordinary composition of general-purpose primitives available to anyone willing to reach for ip netns, ip link, and the rest of this chapter's own small, reusable command vocabulary directly.