A Linux namespace is a kernel feature that wraps a global system resource in an abstraction so that the processes inside the namespace see their own isolated instance of that resource. In Linux process management, namespaces decide what a process can see: its process IDs, mount points, network interfaces, hostname, users and more, while the rest of the system keeps a different view.
Namespaces are the isolation foundation of containers. Together with cgroups, which limit how much a process may consume, and security layers such as seccomp and capabilities, they let a single kernel run many workloads that behave as if each had a private machine.
How a Linux namespace isolates processes
Every process in Linux belongs to exactly one namespace of each type at any moment. At boot, all processes share the initial namespaces, and new ones are created only when a process asks the kernel for them. The kernel then answers system calls from that process using the resource instance tied to its namespace rather than the global one.
Three system calls do the work. clone() with flags such as CLONE_NEWPID or CLONE_NEWNET starts a child process inside new namespaces. unshare() moves the calling process into fresh namespaces without creating a child. setns() joins an existing namespace through a file descriptor, which is how tools such as nsenter and docker exec enter a running container.
Each namespace is exposed as a symbolic link under /proc/PID/ns/, for example /proc/1234/ns/net. Two processes whose links point to the same inode number share that namespace. A namespace normally disappears when its last member process exits, but bind-mounting the ns file or holding an open descriptor keeps it alive, which is how ip netns creates persistent network namespaces.
The eight namespace types
Current kernels provide eight namespace types, each isolating one category of resource. They were added gradually, from the mount namespace in kernel 2.4.19 to the time namespace in kernel 5.6, so older systems may lack some of them. The table summarizes what each type isolates.
| Namespace | Clone flag | What it isolates |
|---|---|---|
| Mount | CLONE_NEWNS | Mount points and the file system tree |
| PID | CLONE_NEWPID | Process ID numbering, including a private PID 1 |
| Network | CLONE_NEWNET | Interfaces, routes, firewall rules, sockets, ports |
| UTS | CLONE_NEWUTS | Hostname and NIS domain name |
| IPC | CLONE_NEWIPC | System V IPC objects and POSIX message queues |
| User | CLONE_NEWUSER | User and group IDs and capabilities |
| Cgroup | CLONE_NEWCGROUP | The visible root of the cgroup hierarchy |
| Time | CLONE_NEWTIME | Offsets for monotonic and boot-time clocks |
The PID namespace deserves special attention. The first process inside it becomes PID 1 and inherits the duties of an init process: it must reap orphaned children, and if it exits, the kernel kills every other process in the namespace. PID namespaces are also hierarchical, so a parent namespace can see and signal processes in its children, but not the reverse.
The user namespace maps user IDs inside the namespace to different IDs outside through /proc/PID/uid_map. A process can be root inside the namespace while remaining an unprivileged user on the host. This mapping is what makes rootless containers possible, and it is the only namespace type that an unprivileged user may create on most distributions.
Namespaces compared with cgroups and other isolation tools
Namespaces and cgroups are often mentioned together, but they answer different questions. A namespace controls visibility, while a cgroup controls resource consumption. A container needs both, plus additional hardening, because neither mechanism alone stops a process from abusing the shared kernel.
| Mechanism | Question it answers | Typical example |
|---|---|---|
| Namespaces | What can the process see | Private PID 1 and private network stack |
| Cgroups | How much can the process use | Limit to 2 CPUs and 512 MiB of memory |
| Capabilities | Which privileged operations are allowed | Allow binding to port 80 only |
| Seccomp | Which system calls are allowed | Block mount and kexec calls |
| chroot | Which directory is the root | Older, weaker file system confinement |
| Virtual machine | Which kernel runs the workload | Separate guest kernel on a hypervisor |
The comparison with chroot is instructive. chroot changes only the apparent root directory and can be escaped by a privileged process, whereas a mount namespace combined with pivot_root gives a container a genuinely separate mount table. Compared with a virtual machine, namespaces are far lighter because there is no second kernel, but they also provide a weaker security boundary, since every container shares the host kernel and its vulnerabilities.
Working with namespaces in practice
Administrators rarely call the system calls directly. The util-linux package ships unshare, nsenter and lsns, and iproute2 adds ip netns for network namespaces. Container engines and systemd build on the same primitives, so understanding the command-line tools makes debugging container problems much easier.
| Task | Command | Notes |
|---|---|---|
| List namespaces | lsns | Shows type, inode, process count and command |
| Start a shell in new namespaces | unshare --pid --fork --mount-proc bash | Requires –fork for a new PID namespace |
| Create a rootless sandbox | unshare --user --map-root-user bash | Root inside, ordinary user outside |
| Enter a container’s network | nsenter -t PID -n ip addr | Runs host tools in the target namespace |
| Create a named network namespace | ip netns add test | Persists through a bind mount in /run/netns |
systemd also applies namespaces to ordinary services. Options such as PrivateTmp=yes, PrivateNetwork=yes, ProtectSystem=strict and PrivateUsers=yes place a unit in its own mount, network or user namespace without any container runtime, which is a low-cost hardening step for daemons that do not need broad access.
Limitations and common mistakes
Namespaces isolate views, not everything. Kernel memory, the system clock (outside the time namespace offsets), kernel modules, /proc/sys settings that are not namespaced and many device files remain global. A container that receives broad capabilities or a mounted Docker socket can therefore affect the host despite running in its own namespaces.
- Forgetting –fork with
unshare --pid: the calling process stays in the old PID namespace, and only its children enter the new one. - Running a shell as PID 1 without an init: zombie processes accumulate because the shell does not reap orphans; a minimal init such as tini solves this.
- Assuming user namespaces are always safe: they expose more kernel code to unprivileged users, so some distributions restrict them with sysctl settings or security modules.
- Reading host data through /proc: without a separate mount of /proc, tools inside a new PID namespace still show host processes.
The deeper point is that a namespace is a building block, not a sandbox. Strong isolation comes from layering namespaces, cgroups, dropped capabilities, seccomp filters and a mandatory access control system such as SELinux or AppArmor.
A plain-language summary
Imagine an office building where every team works in a room with frosted windows. Each team has its own phone extensions, its own whiteboard and its own door sign, and from inside the room it looks as if the team is alone in the building. The building, its electricity and its foundations are still shared.
A Linux namespace is that frosted room for programs. It lets a group of programs believe they have their own list of running programs, their own network and their own files, while one shared operating system kernel quietly serves everyone. This is the trick that makes containers possible, and it is cheap because no second computer has to be simulated.
An example from practice
A network engineer needs to test a new routing configuration on a production server without disturbing live traffic. Instead of starting a virtual machine, the engineer runs ip netns add lab and creates a virtual Ethernet pair, placing one end in the new namespace. Inside it, the engineer assigns the address 10.200.0.2/24, adds test routes and loads experimental firewall rules.
Because the lab namespace has its own routing table and firewall, mistakes there cannot remove the server’s real default route. Tests with ip netns exec lab ping and curl complete in a few minutes, and ip netns delete lab removes everything afterwards. The outcome is a realistic test on the real kernel with no downtime and no extra virtual machine to maintain.