Linux Namespaces Deep Dive
A hands-on guide to all seven Linux namespace types — mnt, pid, net, user, uts, ipc, cgroup — plus unshare and nsenter with concrete shell examples.
Before you start
- ▸Linux kernel 4.6 or newer (check with uname -r)
- ▸util-linux package installed for unshare, nsenter, and lsns
- ▸iproute2 installed for network namespace examples
- ▸sudo or root access for most namespace types (user namespaces excepted)
Linux namespaces are the kernel mechanism that makes containers possible. Each namespace wraps a specific global resource so that processes inside see their own isolated view of it. Understanding namespaces at the syscall and command level lets you build containers from scratch, debug runaway container configurations, and tune isolation for performance-sensitive workloads. This guide walks through all seven namespace types, then covers the two most important userspace tools: unshare and nsenter.
The Seven Namespace Types
The kernel currently defines these namespace types, each controlled by a flag passed to clone(2), unshare(2), or setns(2):
| Type | Flag | Isolates |
|---|---|---|
| Mount | CLONE_NEWNS | Filesystem mount points |
| PID | CLONE_NEWPID | Process ID tree |
| Network | CLONE_NEWNET | NICs, routes, iptables rules, ports |
| User | CLONE_NEWUSER | UID/GID mappings |
| UTS | CLONE_NEWUTS | Hostname and NIS domain name |
| IPC | CLONE_NEWIPC | SysV IPC, POSIX message queues |
| Cgroup | CLONE_NEWCGROUP | cgroup root view (Linux 4.6+) |
Prerequisites and Kernel Check
You need a kernel ≥ 4.6 for all seven types. Verify your running kernel and that the relevant options are compiled in:
uname -r
grep -E 'CONFIG_NAMESPACES|CONFIG_UTS_NS|CONFIG_IPC_NS|CONFIG_USER_NS|CONFIG_PID_NS|CONFIG_NET_NS|CONFIG_CGROUPS' /boot/config-$(uname -r)
Every modern LTS kernel (Ubuntu 22.04, Debian 12, Fedora 39+, RHEL 9) has all namespace types enabled. Install the utilities:
# Debian/Ubuntu
sudo apt install util-linux iproute2
# Fedora/RHEL/Rocky
sudo dnf install util-linux iproute
# Arch
sudo pacman -S util-linux iproute2
UTS Namespace — Hostname Isolation
The UTS namespace is the simplest entry point. Use it to give a process its own hostname without touching the host:
sudo unshare --uts /bin/bash
hostname container-node
hostname
Open a second terminal and run hostname there — the host name is unchanged. The --uts flag calls unshare(CLONE_NEWUTS) before exec-ing the shell. Exit the shell to discard the namespace.
IPC Namespace — Shared Memory Isolation
IPC namespaces isolate SysV message queues, semaphores, and shared memory segments. Processes in different IPC namespaces cannot communicate through these mechanisms:
# In the host: create a message queue
ipcmk -Q
# Now enter a new IPC namespace
sudo unshare --ipc /bin/bash
ipcs -q # Empty — host queues are invisible
Mount Namespace — Filesystem Isolation
A new mount namespace starts as a copy of the parent's mount table. Changes to mounts inside do not propagate out (unless the mountpoint is shared). This is what gives containers their private root filesystems.
sudo unshare --mount /bin/bash
mount -t tmpfs tmpfs /mnt
findmnt /mnt # Visible inside
In another terminal, findmnt /mnt shows nothing. Important: by default mount namespaces inherit the host's peer group and propagate MS_SHARED events. To truly isolate, remount root as private first:
mount --make-rprivate /
mount -t tmpfs tmpfs /mnt # Now completely invisible to host
PID Namespace — Process Tree Isolation
Inside a new PID namespace, the first process gets PID 1 and acts as init for that namespace. The host can still see the process under its original PID; the namespace view is layered on top.
sudo unshare --pid --fork --mount-proc /bin/bash
ps aux # Only shows processes inside the namespace
echo $$ # Prints 1
--fork is required because unshare itself cannot become PID 1 after calling unshare(2) — the first child becomes PID 1. --mount-proc remounts /proc inside the namespace so tools like ps see the correct data.
From the host, find the real PID:
ps -eo pid,pidns,comm | grep bash
Network Namespace — Full Network Stack Isolation
A new network namespace has only a loopback interface (down by default). This is what container runtimes use before wiring up veth pairs.
sudo unshare --net /bin/bash
ip link list # Only lo, and it is DOWN
ip link set lo up
ping -c1 127.0.0.1 # Works after bringing lo up
Connecting two network namespaces with a veth pair
Create a named namespace so you can reference it with ip netns:
sudo ip netns add red
sudo ip link add veth0 type veth peer name veth1
sudo ip link set veth1 netns red
# Host side
sudo ip addr add 10.0.0.1/24 dev veth0
sudo ip link set veth0 up
# Inside the namespace
sudo ip netns exec red ip addr add 10.0.0.2/24 dev veth1
sudo ip netns exec red ip link set veth1 up
sudo ip netns exec red ip link set lo up
# Test connectivity
sudo ip netns exec red ping -c2 10.0.0.1
Clean up with sudo ip netns del red.
User Namespace — Unprivileged Containers
User namespaces let an unprivileged user map their UID/GID to root inside the namespace. This is the foundation of rootless containers (Podman, rootless Docker).
# No sudo needed
unshare --user --map-root-user /bin/bash
whoami # root
id # uid=0(root) gid=0(root) — inside the namespace
cat /proc/self/uid_map # Shows the mapping
The kernel maps your real UID (e.g. 1000) to UID 0 inside the namespace. Outside, the process is still your unprivileged user. On RHEL/Rocky 8, user namespaces may be restricted by sysctl:
# Check
sysctl user.max_user_namespaces
# Enable if zero (not persistent — write to /etc/sysctl.d/ for persistence)
sudo sysctl -w user.max_user_namespaces=28633
Cgroup Namespace — Isolated cgroup Root
A cgroup namespace changes what a process sees as / in the cgroup hierarchy. This prevents a containerised process from walking up the host's cgroup tree:
sudo unshare --cgroup /bin/bash
cat /proc/self/cgroup # Shows / as the root, not the full host path
Container runtimes combine cgroup namespaces with cgroup v2 resource limits to both restrict and hide resource accounting from the contained process.
Combining Namespaces — A Minimal Container
Real containers combine all namespace types in a single unshare call:
sudo unshare \
--mount \
--uts \
--ipc \
--pid \
--net \
--cgroup \
--fork \
--mount-proc \
/bin/bash
hostname minimal-container
ps aux
ip link
You now have a fully isolated shell with its own mount table, process tree, network stack, hostname, IPC objects, and cgroup view — built entirely from shell commands.
nsenter — Joining Existing Namespaces
nsenter attaches a new process to the namespaces of an already-running process. This is the primary tool for debugging containers without a working shell inside them.
# Find the PID of a running container's init process
sudo docker inspect --format '{{.State.Pid}}' my-container
# or for a systemd-nspawn unit:
MACHINECTL show my-machine -p Leader --value
# Enter all namespaces of PID 12345
sudo nsenter --target 12345 --mount --uts --ipc --net --pid -- /bin/bash
You can enter a subset of namespaces. For example, enter only the network namespace to run tcpdump against a container's interfaces without disturbing its filesystem:
sudo nsenter --target 12345 --net -- tcpdump -i eth0 -nn
Namespace file descriptors live in /proc/<pid>/ns/. You can also pass them directly to nsenter via --mount=/proc/12345/ns/mnt, or keep a namespace alive by bind-mounting its fd even after all processes inside exit:
sudo touch /run/netns/preserved
sudo mount --bind /proc/12345/ns/net /run/netns/preserved
Inspecting Namespaces
Check which namespaces a process belongs to:
ls -la /proc/$$/ns/
# lrwxrwxrwx 1 user user 0 cgroup -> cgroup:[4026531835]
# lrwxrwxrwx 1 user user 0 ipc -> ipc:[4026531839]
# ... and so on
Two processes sharing an inode number for a namespace type are in the same namespace. Compare them:
stat -L /proc/1/ns/net /proc/$$/ns/net
Use lsns (from util-linux) for a system-wide namespace inventory:
sudo lsns
sudo lsns --type net
Troubleshooting
unshare: unshare failed: Operation not permitted— You need eitherCAP_SYS_ADMIN(usesudo) or, for user namespaces only, checksysctl user.max_user_namespacesis non-zero.- PID namespace:
psstill shows host processes — You forgot--mount-proc./procstill reflects the host. Re-run with that flag or manuallymount -t proc proc /procinside. - Network namespace: no connectivity after veth setup — Check
ip linkon both sides; both ends must be UP and IPs must be in the same subnet. Also verify host IP forwarding:sysctl net.ipv4.ip_forward. nsentertarget process exited — The namespace file descriptors in/proc/<pid>/ns/disappear when the last process exits and no bind-mount holds them. Bind-mount early if you need persistent namespaces.- User namespace mapping fails on Debian — Debian restricts unprivileged user namespaces via
kernel.apparmor_restrict_unprivileged_userns(Debian 12+). Set it to 0 or usesudo.
Frequently asked questions
- Do I need root to create namespaces?
- Most namespace types require CAP_SYS_ADMIN (effectively root). User namespaces are the exception — an unprivileged process can create them on most distros, which is what enables rootless container runtimes like Podman.
- What is the difference between unshare and ip netns?
- unshare creates a new namespace and executes a process inside it as an anonymous namespace. ip netns creates a named network namespace persisted as a bind-mount under /run/netns/, making it addressable by name from other tools without needing a running process.
- How do container runtimes like Docker use namespaces?
- Docker's containerd/runc calls clone(2) with all CLONE_NEW* flags simultaneously, then applies cgroup limits, sets up veth pairs for networking, and pivots the root filesystem — producing what users see as a container. nsenter replicates the join step.
- Can a process escape its namespace?
- A process with CAP_SYS_ADMIN in the host user namespace can call setns(2) to re-enter any namespace. A process confined to a user namespace without that capability cannot escape. This is why container security also depends on seccomp profiles and AppArmor/SELinux policies.
- Why does ps show all host processes even after unshare --pid?
- Because /proc is still the host's procfs. You must remount it inside the new PID namespace with mount -t proc proc /proc, or use the --mount-proc flag with unshare, so the kernel populates it relative to the new PID namespace root.
Related guides
AI and Artificial-Life Tools on Linux
Set up open-source AI/ML and artificial-life toolkits on Linux: PyTorch, JAX, DEAP, Avida, NetLogo, and RL environments with GPU driver guidance.
Assembly Language on Linux: A Starter Guide
Write x86-64 assembly on Linux from scratch: install NASM and GAS, learn syscalls, assemble and link a working program, then inspect and debug it.
How to Benchmark Disk Performance with fio
Learn to benchmark Linux disk performance with fio: writing job files, testing latency and throughput, and interpreting IOPS and percentile output correctly.
The Linux Boot Process Explained
Trace the full Linux boot sequence from UEFI firmware through GRUB2, the kernel, initramfs, and systemd to your login prompt — with diagnostics at each stage.