Andrew Mercer
on this page

This is one half of a two-part series. This document covers network namespaces — the isolation primitive. The companion document, Open vSwitch: A Deep Dive, covers the programmable switch that's typically used to wire many namespaces (or VMs, or containers) together at scale. The two intersect most directly in that document's Combining Namespaces with OVS section, and in Section 3.4 below.

A third, hands-on companion — "Getting Started with Netns and OVS: A Workshop Guide" — walks through both of these as guided labs if you'd rather learn by typing commands than reading theory.

Table of Contents

  1. Introduction & Mental Model
  2. Network Namespaces: Fundamentals
  3. Connecting Namespaces: veth Pairs and Bridges
  4. Routing, NAT, and External Connectivity
  5. Observability and Troubleshooting
  6. Security Considerations
  7. Real-World Contexts
  8. Command Reference / Cheat Sheet
  9. Further Reading

Not the same as Kubernetes namespaces. Despite the name, Linux network namespaces (a kernel isolation primitive) and Kubernetes namespaces (a logical grouping for API objects) are unrelated concepts that happen to share a name. A Kubernetes Pod does get its own Linux network namespace under the hood, but "namespace" in kubectl get namespaces refers to something else entirely.

1. Introduction & Mental Model

Network namespaces (netns) are a kernel primitive that gives a process its own private copy of the network stack: interfaces, routing tables, iptables/nftables rules, sockets, /proc/net, everything. This is the isolation layer that Docker, Kubernetes, LXC, and ip netns all build on.

A namespace in isolation only answers "how do I get an isolated network stack?" It does not, by itself, answer "how do I wire many of these together, tag/route/tunnel their traffic, and do it at scale with policy?" That second question is what Open vSwitch — a production-quality, programmable software switch — is for. You will very often see the two used together: containers/VMs live in namespaces, and a switch (a plain Linux bridge, or OVS for anything beyond the basics) is the fabric connecting those namespaces to each other and to the physical network.

This document treats namespaces as a self-contained topic: what they are, how to create and inspect them, how to connect a handful of them with simple tools (veth pairs, the Linux bridge), and how to route traffic in and out. When your needs outgrow a plain Linux bridge — VLANs, tunnels, programmable flow rules, centralized control — the natural next step is the companion Open vSwitch: A Deep Dive.

2. Network Namespaces: Fundamentals

2.1 What a namespace actually isolates

A network namespace is one of several Linux namespace types (alongside PID, mount, UTS, IPC, user, cgroup). Each network namespace has its own:

  • Network interfaces (except those explicitly shared/moved between namespaces)
  • IPv4 and IPv6 routing tables
  • iptables/nftables/ipset state
  • Socket namespace (so two namespaces can each bind port 80 independently)
  • /proc/net/* and /sys/class/net/* views
  • Netfilter conntrack table

Interfaces themselves belong to exactly one namespace at a time (with the exception of a few virtual pairings designed to cross the boundary, covered in Section 3). The loopback interface (lo) exists in every namespace but starts down — a very common gotcha for beginners.

2.2 The default namespace

Every Linux system boots with an initial network namespace (sometimes called the "root" or "default" namespace) that owns all physical NICs at boot. Additional namespaces are always created empty and populated by moving or creating interfaces into them.

2.3 Creating and inspecting namespaces

The ip netns subcommand (part of iproute2) is the standard tool:

# Create a namespace
sudo ip netns add red

# List namespaces
ip netns list

# Delete a namespace
sudo ip netns delete red

Under the hood, ip netns add red does two things: it calls unshare(CLONE_NEWNET) to create a new network namespace, and it bind-mounts that namespace's handle at /var/run/netns/red so it persists even with no process running inside it. This bind-mount trick is exactly why namespaces created this way outlive any single process — they're "pinned" to a filesystem path.

2.4 Running commands inside a namespace

sudo ip netns exec red ip addr show
sudo ip netns exec red bash        # interactive shell inside the namespace

Everything you run this way sees only that namespace's network stack. Note lo is down by default:

sudo ip netns exec red ip link set lo up

2.5 Namespaces without ip netns: unshare and container runtimes

You don't need ip netns to create a namespace — it's just a convenience wrapper with the pinning behavior described above. Two other common paths:

# unshare directly (namespace disappears when the shell exits, unless bind-mounted)
sudo unshare --net bash

Container runtimes (runc, containerd, CRI-O) call the same clone()/unshare() syscalls internally when they start a container's pause/sandbox process. This is why ip netns list on a Kubernetes node often shows nothing — kubelet-managed namespaces usually aren't pinned under /var/run/netns, but you can still reach them via /proc/<pid>/ns/net and nsenter:

sudo nsenter -t <pid> -n ip addr show

nsenter --target <pid> --net is the general-purpose tool for entering any process's namespace, pinned or not — it's what crictl exec/docker exec effectively wrap.

2.6 Namespace lifetime and reference counting

A network namespace is kept alive by the kernel as long as something references it: a running process inside it, an open file descriptor to /proc/<pid>/ns/net, a bind-mount (as ip netns add creates), or an interface still assigned to it. This matters operationally — deleting a bind-mount with ip netns delete does not forcibly kill processes still running inside it; they keep running, and the namespace itself lingers until they exit.

3. Connecting Namespaces: veth Pairs and Bridges

A namespace in isolation is useless without a way in or out. The fundamental connective tissue is the veth pair: two virtual Ethernet interfaces that are always created together and act like a virtual patch cable — anything sent in one end appears at the other.

3.1 Creating a veth pair

sudo ip link add veth-red type veth peer name veth-red-br

veth-red and veth-red-br are now linked. Move one end into a namespace:

sudo ip link set veth-red netns red

Now veth-red lives inside the red namespace, while veth-red-br remains in the root namespace (or wherever you run this from).

3.2 Point-to-point: two namespaces, one veth pair

The simplest topology: put each end of a veth pair into a different namespace, address both ends, and you have direct connectivity between two isolated stacks:

sudo ip netns add red
sudo ip netns add blue
sudo ip link add veth-red type veth peer name veth-blue
sudo ip link set veth-red netns red
sudo ip link set veth-blue netns blue

sudo ip netns exec red ip addr add 10.10.10.1/24 dev veth-red
sudo ip netns exec red ip link set veth-red up
sudo ip netns exec red ip link set lo up

sudo ip netns exec blue ip addr add 10.10.10.2/24 dev veth-blue
sudo ip netns exec blue ip link set veth-blue up
sudo ip netns exec blue ip link set lo up

sudo ip netns exec red ping -c 2 10.10.10.2

This works for exactly two namespaces. For more than two, you need a switch of some kind in the middle — either a Linux bridge (below) or an OVS bridge (see the Open vSwitch guide).

3.3 Many namespaces via a Linux bridge

sudo ip link add br0 type bridge
sudo ip link set br0 up

for ns in red blue green; do
  sudo ip netns add $ns
  sudo ip link add veth-$ns type veth peer name veth-$ns-br
  sudo ip link set veth-$ns netns $ns
  sudo ip link set veth-$ns-br master br0
  sudo ip link set veth-$ns-br up
  sudo ip netns exec $ns ip link set lo up
  sudo ip netns exec $ns ip link set veth-$ns up
done

sudo ip netns exec red  ip addr add 10.10.10.1/24 dev veth-red
sudo ip netns exec blue ip addr add 10.10.10.2/24 dev veth-blue
sudo ip netns exec green ip addr add 10.10.10.3/24 dev veth-green

This is precisely what Docker's default bridge network (docker0) does under the hood, and roughly what CNI bridge-mode plugins do for Kubernetes pods on a single node.

3.4 Why reach for a real switch fabric?

The Linux bridge is a perfectly good L2 switch for simple cases. You reach for Open vSwitch instead when you need any of:

  • Programmatic, atomic flow-based forwarding rules (OpenFlow) instead of static learning-bridge behavior
  • VLAN tagging/trunking with more flexibility, or per-port VLAN modes
  • Native tunnel port types (VXLAN, GRE, Geneve) without extra kernel modules or ip link tunnel juggling
  • Centralized control across many hosts (SDN controllers, OVN)
  • Rich telemetry (sFlow, NetFlow/IPFIX, mirroring) built in
  • QoS queues and rate limiting per-port
  • A programmatic database (OVSDB) for automation instead of scraping ip output

The good news: everything you just learned about veth pairs and moving interfaces into namespaces carries over unchanged — OVS just replaces the Linux bridge as the thing the other end of the veth plugs into. See Combining Namespaces with OVS in the companion guide for the worked example.

4. Routing, NAT, and External Connectivity

4.1 Giving a namespace internet access

A namespace with only a veth end has no path to the outside world unless something routes and NATs for it. The classic pattern: attach the "outside" end of the veth pair to a bridge that also holds a route to the internet, or route directly through the root namespace.

# Root namespace acts as router for 'red'
sudo sysctl -w net.ipv4.ip_forward=1

sudo ip netns exec red ip route add default via 10.10.10.254

sudo ip addr add 10.10.10.254/24 dev veth-red-br   # the root-side veth end
sudo ip link set veth-red-br up

# NAT so 10.10.10.0/24 can reach the internet via the host's uplink (eth0)
sudo iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -o eth0 -j MASQUERADE

4.2 Namespaces as routers themselves

Because each namespace has its own routing table, you can build multi-hop topologies entirely out of namespaces and veth pairs — a namespace with two interfaces and ip_forward=1 set (inside that namespace) acts as a router between two "networks," which is a great way to practice routing/subnetting without touching physical hardware or VMs. This is a staple of network-engineering training labs (see the workshop companion doc).

4.3 Persisting namespace networking across reboots

ip netns state is not persistent by default — it's created imperatively and vanishes on reboot. For anything long-lived, either:

  • Script the setup in a systemd unit / network-scripts hook, or
  • Use a higher-level tool (Docker, Kubernetes, libvirt) that manages this lifecycle for you, or
  • Use netplan/systemd-networkd namespace support where available.

5. Observability and Troubleshooting

This section covers namespace-side troubleshooting. For diagnosing the switch fabric itself once you've introduced OVS, see the Open vSwitch guide's Observability and Troubleshooting section.

ip netns list
sudo ip netns exec red ip addr show
sudo ip netns exec red ip route show
sudo ip netns exec red ss -tulnp
sudo nsenter -t <pid> -n ip addr show     # for unpinned namespaces (containers)

tcpdump works fine inside a namespace:

sudo ip netns exec red tcpdump -i veth-red

Common beginner pitfalls

  • Forgetting to bring lo up inside a namespace (breaks anything binding to 127.0.0.1, including some daemons that misbehave without it)
  • Forgetting net.ipv4.ip_forward=1 when a namespace or the root host is meant to route between segments
  • Expecting a veth end to "just work" before it's been moved into the target namespace and brought up on both ends

6. Security Considerations

Namespace isolation is strong but not absolute. A process with CAP_SYS_ADMIN (or more precisely CAP_NET_ADMIN plus a way to call setns()) can potentially move between namespaces if it has a file descriptor to another namespace's handle. Container escapes have historically abused adjacent primitives; don't treat netns alone as a hard security boundary equivalent to a VM.

Once traffic leaves a namespace and enters a shared switch fabric, additional isolation and firewalling decisions move to that fabric — see the Open vSwitch guide's Security Considerations for how VLANs, flow-table ACLs, and stateful conntrack rules pick up where namespace isolation leaves off.

7. Real-World Contexts

  • Kubernetes: every Pod gets its own network namespace (created by the container runtime, wired up by whatever CNI plugin is configured). Bridge-mode CNIs use Linux bridges; OVN-Kubernetes and Antrea use OVS bridges per node — see the Open vSwitch guide for that half of the picture.
  • OpenStack Neutron: compute nodes give each VM's tap interface (conceptually similar to a namespace's veth end) a place to plug into the local switch fabric — again, detailed on the OVS side in the companion guide.
  • CI/testing sandboxes: namespaces are a cheap way to give parallel test runners fully isolated loopback/port space without containers or VMs — useful for testing network-facing code that needs to bind fixed ports repeatedly and in parallel.
  • Home labs: exactly what the workshop companion document walks through — a safe, disposable playground to internalize these concepts on a single machine or single VM, with zero risk to production infrastructure.

8. Command Reference / Cheat Sheet

Namespaces

ip netns add <name>
ip netns delete <name>
ip netns list
ip netns exec <name> <command>
ip netns exec <name> bash
nsenter -t <pid> -n <command>

veth pairs

ip link add <a> type veth peer name <b>
ip link set <a> netns <name>
ip link set <iface> up

Linux bridge

ip link add <br> type bridge
ip link set <iface> master <br>
ip link set <br> up
bridge fdb show

Routing / NAT

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -s <cidr> -o <uplink> -j MASQUERADE

For the OVS equivalent of bridge/port management and flow rules, see the Open vSwitch guide's cheat sheet.

Tip: tagging your shell prompt while inside a namespace

Easy to lose track of which namespace a shell is in once you've ip netns exec'd into a few of them. Tag the prompt on entry:

export my_namespace="red"
ip netns exec "${my_namespace}" bash
export my_namespace="red"   # re-export inside the new shell — it doesn't inherit
export PS1='[${my_namespace}]\$ '

9. Further Reading

  • man ip-netns, man nsenter, man unshare — the man pages are genuinely excellent and authoritative for exact syntax.
  • The iproute2 documentation (ip-link(8), ip-address(8), ip-route(8)) for everything namespace-adjacent.
  • Continue to Open vSwitch: A Deep Dive for the programmable switching layer that typically connects namespaces at scale.
  • For hands-on practice, work through the companion document: "Getting Started with Netns and OVS: A Workshop Guide."