On this page
Namespaces
Namespaces are the fundamental primitives of Linux containers—each namespace provides a process with an illusion of resource isolation: PID namespaces make processes inside a container believe they are PID 1, user namespaces map the container’s root to a regular user on the host, and network namespaces allow each container to see its own network interfaces and routing tables.
nsproxybinds multiple namespaces into a single process's "isolated view". Kernel version: 2.6 ~ 6.x
Overview
Namespaces are the fundamental primitives of Linux containers—each namespace provides processes within it with an illusion of resource isolation. A process sees itself as PID 1 in a PID namespace, sees an independent filesystem tree in a mount namespace, and sees independent network interfaces and routing tables in a network namespace.
The complementary relationship between namespaces and cgroups: namespaces isolate visibility (what you see), while cgroups limit resource quantity (how much you can use). Docker/LXC/Podman are built on top of the stack of namespaces + cgroups + seccomp.
8 Types of Namespaces
| Type | Clone Flag | Isolated Content | Introduced |
|---|---|---|---|
| Mount | CLONE_NEWNS | Filesystem mount points; each can mount/umount independently | 2.4.19 |
| UTS | CLONE_NEWUTS | hostname, domainname | 2.6.19 |
| IPC | CLONE_NEWIPC | SysV IPC (shm/sem/msg), POSIX mq | 2.6.19 |
| PID | CLONE_NEWPID | Process IDs; PID 1 within the namespace sees itself as init | 2.6.24 |
| Network | CLONE_NEWNET | Network interfaces, routing, netfilter, sockets | 2.6.24 (completed in 2.6.29) |
| User | CLONE_NEWUSER | UID/GID mapping (root in namespace = regular user on host) | 3.8 |
| Cgroup | CLONE_NEWCGROUP | Cgroup root directory (container cannot see host cgroup tree) | 4.6 |
| Time | CLONE_NEWTIME | CLOCK_MONOTONIC/BOOTTIME offset (each namespace has independent time) | 5.6 |
Kernel Implementation: nsproxy
// include/linux/nsproxy.h
;
Each task_struct → nsproxy points to the various namespaces to which the process belongs. When fork() is called, the child process shares the parent's nsproxy (count++). When unshare() is called, the nsproxy is cloned, and the specified type of namespace is replaced with a newly created one. setns(fd) joins the current process to an existing namespace (the file descriptor comes from /proc/<pid>/ns/<type>).
PID Namespace: Nesting and the Init Process
PID namespaces form a nested tree—the parent namespace can see processes in child namespaces (with regular PIDs in the parent namespace), while child namespaces cannot see processes in the parent namespace.
Each PID namespace has its own init process (PID 1). The special responsibilities of PID 1 include:
- Orphan adoption: If a parent process exits before its child → the child is re-parented to PID 1
- wait() loop: PID 1 must actively
wait()to reap zombies; otherwise, zombies accumulate within the namespace until PIDs are exhausted - Cannot be killed: SIGKILL is ineffective against PID 1 (unless sent from an ancestor namespace). However, PID 1 can be suspended with SIGSTOP
- PID 1 exit → entire namespace is destroyed: The kernel sends SIGKILL to all processes in the namespace
The PID 1 in a container is usually the application itself (e.g., CMD ["nginx", "-g", "daemon off;"])—it must correctly handle signals (SIGTERM) and orphan reaping. If it does not, zombie processes will accumulate.
User Namespace: root in namespace = nobody on host
User namespaces are the foundation of container security—they allow unprivileged users to have root identity within the namespace:
/proc/<pid>/uid_map:
ns_uid 0 → host_uid 1000 # root in namespace = regular user on host
ns_uid 1 → host_uid 100000 # regular user in namespace = subuid starting from 100000 on host
# Unprivileged user creates a user namespace (kernel.unprivileged_userns_clone=1):
$ unshare --user --map-root-user
# → Inside the namespace, you see yourself as root, but on the host you remain a regular user
Key security feature: "root" within a user namespace can only operate on resources owned by that user namespace—it cannot access host files, load kernel modules, or kill host processes. This "seeming root, actually jailed" behavior makes rootless containers possible (Podman rootless mode).
Mount Namespace: Independent Mount Tree
Each mount namespace has its own list of filesystem mount points. A mount operation in one namespace does not affect others. When CLONE_NEWNS is used to create a new namespace, the child namespace receives a copy (not shared) of the parent's mount points—after which they are modified independently.
Shared subtrees (shared subtrees, 2.6.15+): mount --make-shared /path causes mount events to propagate across multiple namespaces. Systemd uses this mechanism to share /run between the host and containers.
Network Namespace: Independent Network Stack
The network namespace (netns) is the most commonly used namespace—each container requires one. Each netns has its own:
- List of network interfaces
- Routing table
- Netfilter rules
- Socket bindings
ip netns add myns creates a new netns (which is actually a bind mount of /var/run/netns/myns). ip link set eth0 netns myns moves a network interface into the netns.
User-space API
# Create new PID + mount + net namespaces:
# --fork: Required — otherwise the init of the new PID namespace remains the original shell
# --mount-proc: Mount procfs in the new namespace → ps will only show processes within the namespace
# Enter an existing namespace:
# List namespaces:
References
- Source code:
kernel/nsproxy.c,kernel/pid_namespace.c,kernel/user_namespace.c,net/core/net_namespace.c,fs/namespace.c - Kernel documentation:
Documentation/admin-guide/namespaces/ - LWN: "Namespaces in Linux" series
Keywords: namespace, nsproxy, PID namespace, user namespace, UID mapping, mount namespace, network namespace, CLONE_NEWNS, container, setns, unshare