The most surprising thing about Linux container namespaces is that they don’t actually create new resources, they just hide existing ones and present a limited view of the system.

Let’s see this in action. We’ll start by looking at the process tree from the host’s perspective.

$ ps auxf
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.1 168588  9876 ?        Ss   Jul20   0:10 /sbin/init
root           2  0.0  0.0      0     0 ?        S    Jul20   0:00 [kthreadd]
root           3  0.0  0.0      0     0 ?        I<   Jul20   0:00 [rcu_gp]
...
johndoe     1234  0.0  0.1 200560 10200 ?        Ss   10:00   0:05 /usr/lib/systemd/systemd --user
johndoe     1235  0.0  0.0 150000  3000 ?        S    10:00   0:00  \_ /usr/lib/systemd/systemd --user --session
johndoe     1236  0.0  0.0 130000  2500 pts/0    Ss   10:01   0:00 /bin/bash
johndoe     1237  0.0  0.0 110000  1800 pts/0    R+   10:05   0:00  \_ ps auxf

Now, let’s enter a new PID namespace. We’ll use unshare for this.

$ sudo unshare --pid --mount-proc bash

Inside this new namespace, the bash process we just started is PID 1.

# ps auxf
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.1 168588  9876 ?        Ss   10:06   0:00 /bin/bash
root           2  0.0  0.0 110000  1800 pts/0    R+   10:06   0:00 ps auxf

Notice how the ps auxf command inside the namespace only shows processes that are also within that PID namespace, or are ancestors. The original PID 1 (/sbin/init) from the host is gone from view. The bash process we started is now PID 1 within this new namespace. This is the PID namespace in action: it isolates the process ID tree. A process inside the namespace sees itself as PID 1, and all other processes within that namespace are numbered relative to it.

This isolation extends to other namespaces as well. The network namespace isolates network interfaces, IP addresses, routing tables, and firewall rules. A container in its own network namespace will have its own eth0 (or similar), its own IP address, and its own routing table, completely separate from the host’s network stack.

The mount namespace controls the filesystem mount points. When a process is in a new mount namespace, it gets a copy of the parent’s mount table. Any mounts or unmounts it performs only affect its own view of the filesystem, not the host’s. This is how containers can have their own /tmp or /var directories that don’t interfere with the host.

The IPC namespace isolates System V IPC objects and POSIX message queues. Processes in different IPC namespaces can have the same IPC identifiers (like semaphores or shared memory segments) but refer to entirely different objects.

The UTS namespace allows a container to have its own hostname and domain name, independent of the host. This is why you can docker run --hostname my-container ubuntu and hostname inside the container will report my-container.

Finally, the user namespace allows a process to have a different set of user and group IDs inside the namespace than it does outside. This is a powerful security feature, allowing a process to run as root (PID 1) inside a container without actually being root on the host system.

The true power of namespaces is how they combine to create the illusion of a separate system. Each namespace provides a specific boundary, and when layered together, they form the foundation of containerization. A container runtime like Docker or Podman orchestrates the creation and management of these namespaces, along with other Linux features like cgroups (for resource limiting) and capabilities (for fine-grained privilege control), to provide a secure and isolated environment.

What most people don’t realize is that the process inside a container, even if it’s PID 1 and running as root within its PID namespace, is still just a regular process on the host kernel. It’s not a separate kernel or a virtual machine. The kernel is shared, but the namespaces provide the isolation boundaries that make it look like a separate system. This is why containers are so lightweight compared to virtual machines – they share the host’s kernel and don’t require a full operating system image to boot.

The next logical step after understanding isolation is learning how to control the resources these isolated processes can consume, which leads directly into Linux Control Groups (cgroups).

Want structured learning?

Take the full Linux & Systems Programming course →