Skip to content
Network Namespace
step 1/5

Reading — step 1 of 5

Read

~3 min readLinux Namespaces

Network Namespace

The third lie: the network. A container gets its own network stack — interfaces, IP addresses, routing table, firewall rules, and (the part every developer has felt) its own port space. Two containers can both bind port 8080 without conflict because, as far as the kernel's concerned, they're binding ports on different machines.

A fresh stack is an empty stack

Create a network namespace (CLONE_NEWNET) and the process finds itself on a machine with one interface: lo (loopback, 127.0.0.1) — and even that starts down. No eth0, no routes, no connectivity. Everything else the container will have must be constructed and plumbed in explicitly.

That emptiness is the security model. The container can't reach the network because there is no network in its universe — not because a firewall says no. Same theme, third verse: PID (processes unaddressable), mount (paths unlisted), network (wires absent).

What lives inside the namespace

Each network namespace owns, independently:

  • Interfaces — a physical or virtual NIC lives in exactly one namespace at a time (it can be moved between them).
  • IP addresses — assigned to interfaces, so they're namespaced too.
  • Routing table — "to reach X, send via Y" decisions, private per namespace.
  • The port table — bindings like 0.0.0.0:8080. This is why the classic EADDRINUSE disappears between containers: the bind tables are simply different tables.
  • Firewall rules (iptables/nftables) — per namespace, which is why in-container rules don't affect the host.

The complete state of "being a machine on a network," duplicated per container, by one kernel.

Getting packets in and out

An isolated stack is uselessly pure — real containers talk. The kernel's building block is the veth pair (virtual ethernet): two interfaces created as a unit, permanently joined like a two-ended cable — packet in one end, out the other. Standard container plumbing:

  1. Create a veth pair on the host.
  2. Move one end into the container's namespace (it becomes the container's eth0).
  3. Attach the other end to a host-side bridge (a virtual switch, typically docker0) — connecting many containers' cables into one LAN.
  4. NAT at the host boundary translates container addresses to the host's for the outside world, and port publishing (-p 8080:80) adds a rule steering host-port traffic to a container address.

That's the topology behind every docker run -p — and it's precisely what the veth + Bridge + NAT lesson (and its exercise) has you build two lessons from now. Today's model is the prerequisite: namespaces as separate stacks, connectivity as explicit wiring between them.

Your exercise: Network Namespace Simulator

Simulate the ownership rules: namespaces with their own interface lists and port bindings; creating/moving interfaces between namespaces (an interface exists in exactly one place — moving it removes it from the source); binds succeeding or failing per-namespace (8080 twice in one namespace fails, across two succeeds); connectivity queries answering only from the local namespace's state. The invariant your tests will probe: nothing is global — every lookup must go through "which namespace am I in?" first. Once that's mechanical, docker ps port columns and EADDRINUSE errors both read as table lookups you've implemented.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…