How Namespaces Work — Process Isolation in Linux

How Namespaces Work — Process Isolation in Linux

2026-03-24

A namespace is a kernel feature that gives a process its own isolated view of a system resource. A process in a PID namespace sees only processes in that namespace. A process in a network namespace has its own IP addresses and routing table. The host kernel manages everything underneath, but the process sees only its slice.

Namespaces are the isolation half of containers. Cgroups are the limits half. Together, they make a process look like a separate machine.

The Seven Namespace Types

Linux provides seven namespace types. Each isolates a different resource.

PID Namespace

The PID namespace gives a process its own process ID numbering. The first process in a new PID namespace becomes PID 1 — the init process of that namespace. It cannot see processes outside its namespace.

On the host, that same process has a different PID — its "real" PID in the host's PID namespace. The kernel maintains both mappings. When you run docker exec to enter a container, the shell you get sees the container's PID numbering.

PID 1 inside a container has a special role: the kernel sends orphaned child processes to it, and SIGTERM/SIGKILL behavior changes. This is why containers need a proper init process — or at least a process that reaps zombie children.

Network Namespace

Each network namespace has its own network interfaces, IP addresses, routing table, firewall rules (iptables/nftables), and socket bindings. A container in its own network namespace cannot see the host's eth0 or any other container's network.

Container runtimes connect a container's network namespace to the outside world using veth pairs — virtual ethernet cables with one end in the container namespace and the other end on a bridge in the host namespace.

Mount Namespace

The mount namespace gives a process its own view of the filesystem. A container can have a completely different / than the host. The container's root is typically an overlayfs mount built from image layers.

The pivot_root syscall switches the process's root directory to the container's filesystem. After that, the host filesystem is invisible — the container cannot access /etc/shadow on the host because its mount namespace does not include the host's /.

User Namespace

The user namespace maps user and group IDs between the container and host. A process can be UID 0 (root) inside the container but UID 100000 on the host. This is the foundation of rootless containers — the container process has full root privileges inside its namespace but zero privileges on the host.

User namespaces are the most important security feature for containers. Without them, root inside a container is root on the host (mitigated only by capability dropping and seccomp). With user namespaces, root inside a container is an unprivileged user on the host — a kernel vulnerability that grants root inside the container grants nothing on the host.

UTS Namespace

UTS (Unix Time Sharing) namespace isolates the hostname and domain name. Each container can have its own hostname, independent of the host. This is why hostname inside a container returns the container ID, not the host's name.

IPC Namespace

The IPC namespace isolates inter-process communication resources: System V shared memory segments, message queues, and semaphores. Processes in different IPC namespaces cannot see each other's shared memory. This prevents a container from reading another container's shared memory segments.

Cgroup Namespace

The cgroup namespace virtualizes the view of the cgroup hierarchy. A process in a cgroup namespace sees its own cgroup as the root of the hierarchy. It cannot see or manipulate cgroups outside its namespace. This prevents a container from inspecting or changing resource limits of other containers.

How Namespaces Are Created

Host Namespace PID 1..N eth0: 10.0.0.1 / (host root) uid 0 = root

clone()

unshare()

setns()

New child process in new namespaces (container start) Current process moves to new namespaces Current process joins existing namespace (exec) Container Namespace PID 1 (init) eth0: 172.17.0.2 / (overlayfs) uid 0 → uid 100000

Three syscalls create and manage namespaces:

clone() creates a new child process in new namespaces. This is how container runtimes start containers. The CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWCGROUP flags specify which namespaces to create. The child process starts in fresh, isolated namespaces.

unshare() moves the calling process into new namespaces without creating a child. This is how the unshare command-line tool works — you can run unshare --pid --fork --mount-proc bash to get a shell with its own PID namespace.

setns() joins an existing namespace. This is how docker exec works — it finds the target container's namespace file descriptors in /proc/<pid>/ns/ and calls setns() to enter each one before executing the command.

Namespace Lifecycle

Namespaces exist as long as at least one process is inside them, or a file descriptor or bind mount keeps them alive. When the last process in a namespace exits, the namespace is destroyed and its resources are freed.

Each process's namespaces are visible in /proc/<pid>/ns/. These are symbolic links to the namespace inodes:

$ ls -la /proc/1/ns/
lrwxrwxrwx 1 root root 0 Mar 24 12:00 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Mar 24 12:00 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Mar 24 12:00 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Mar 24 12:00 net -> net:[4026531969]
lrwxrwxrwx 1 root root 0 Mar 24 12:00 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Mar 24 12:00 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Mar 24 12:00 uts -> uts:[4026531838]

Two processes in the same namespace share the same inode number. Two processes in different namespaces have different inode numbers. This is how tools determine which namespace a process belongs to.

Nesting and Hierarchy

PID and user namespaces can be nested. A container can create namespaces inside itself, creating containers within containers. The parent namespace can see into child namespaces (the host can see all container processes with their host PIDs), but child namespaces cannot see into parent namespaces.

User namespaces have a maximum nesting depth (typically 32 levels). PID namespaces can also nest, with each level having its own PID 1.

Practical Implications

Namespaces explain several container behaviors:

  • Why ps aux inside a container shows few processes — the PID namespace hides everything outside it.
  • Why containers have their own IP addresses — the network namespace provides a separate network stack.
  • Why a container's filesystem changes don't affect the host — the mount namespace provides a separate filesystem view.
  • Why rootless containers are safer — the user namespace makes root inside the container a normal user on the host.
  • Why docker exec can enter a running containersetns() joins the container's existing namespaces.

Next Steps