$linuxjunkies
>

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.

AdvancedUbuntuDebianFedoraArch12 min readUpdated June 7, 2026

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):

TypeFlagIsolates
MountCLONE_NEWNSFilesystem mount points
PIDCLONE_NEWPIDProcess ID tree
NetworkCLONE_NEWNETNICs, routes, iptables rules, ports
UserCLONE_NEWUSERUID/GID mappings
UTSCLONE_NEWUTSHostname and NIS domain name
IPCCLONE_NEWIPCSysV IPC, POSIX message queues
CgroupCLONE_NEWCGROUPcgroup 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 either CAP_SYS_ADMIN (use sudo) or, for user namespaces only, check sysctl user.max_user_namespaces is non-zero.
  • PID namespace: ps still shows host processes — You forgot --mount-proc. /proc still reflects the host. Re-run with that flag or manually mount -t proc proc /proc inside.
  • Network namespace: no connectivity after veth setup — Check ip link on 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.
  • nsenter target 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 use sudo.
tested on:Ubuntu 22.04Fedora 39Debian 12Arch rolling

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