Linux Namespaces: The Building Blocks of Containers
Understand Linux namespaces and how they provide process isolation for containers, with hands-on examples using unshare.
What you'll learn
- ✓What Linux namespaces are and which types exist
- ✓How to create and inspect namespaces with unshare and nsenter
- ✓How containers use namespaces for process isolation
Prerequisites
- •Basic Linux command line
- •Familiarity with Linux processes and PIDs
What Are Namespaces?
Linux namespaces are a kernel feature that partitions system resources so that a set of processes sees one view of the system while another set sees a different view. They are the isolation mechanism that makes containers possible.
Without namespaces, every process on a Linux system shares the same view of the hostname, network interfaces, process IDs, mount points, and users. Namespaces break this shared view, giving each container its own isolated environment.
Together with cgroups (for resource limits) and union filesystems (for layered images), namespaces form the three pillars of Linux container technology.
Types of Namespaces
Linux provides eight types of namespaces, each isolating a different system resource:
| Namespace | Flag | Isolates |
|---|---|---|
| Mount | CLONE_NEWNS | Mount points and filesystems |
| UTS | CLONE_NEWUTS | Hostname and domain name |
| IPC | CLONE_NEWIPC | System V IPC and POSIX message queues |
| PID | CLONE_NEWPID | Process IDs |
| Network | CLONE_NEWNET | Network interfaces, routing, ports |
| User | CLONE_NEWUSER | User and group IDs |
| Cgroup | CLONE_NEWCGROUP | Cgroup root directory |
| Time | CLONE_NEWTIME | System clocks (Linux 5.6+) |
Viewing Namespaces
Every process on Linux belongs to a set of namespaces. You can see them in /proc:
# View namespaces of the current shell
ls -la /proc/$$/ns/
# lrwxrwxrwx 1 user user 0 Jul 2 10:00 cgroup -> cgroup:[4026531835]
# lrwxrwxrwx 1 user user 0 Jul 2 10:00 ipc -> ipc:[4026531839]
# lrwxrwxrwx 1 user user 0 Jul 2 10:00 mnt -> mnt:[4026531841]
# lrwxrwxrwx 1 user user 0 Jul 2 10:00 net -> net:[4026531840]
# lrwxrwxrwx 1 user user 0 Jul 2 10:00 pid -> pid:[4026531836]
# lrwxrwxrwx 1 user user 0 Jul 2 10:00 user -> user:[4026531837]
# lrwxrwxrwx 1 user user 0 Jul 2 10:00 uts -> uts:[4026531838]
# List all namespaces on the system
lsns
# List namespaces for a specific process
lsns -p 1234
The numbers in brackets (like 4026531835) are inode numbers that uniquely identify each namespace. Processes sharing the same inode number are in the same namespace.
Hands-On: UTS Namespace (Hostname Isolation)
The UTS namespace isolates the hostname. Let’s create a new one:
# Create a new UTS namespace and start a shell inside it
sudo unshare --uts /bin/bash
# Inside the new namespace, change the hostname
hostname container-host
hostname
# container-host
# Open a new terminal and check the host's real hostname
hostname
# your-real-hostname (unchanged!)
The hostname change is only visible inside the namespace.
Hands-On: PID Namespace
The PID namespace gives processes their own PID numbering, starting from 1:
# Create a new PID and mount namespace
sudo unshare --pid --mount-proc --fork /bin/bash
# Inside the namespace, PID 1 is our bash process
ps aux
# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# root 1 0.0 0.0 9688 4836 pts/0 S 10:00 0:00 /bin/bash
# root 2 0.0 0.0 12136 3352 pts/0 R+ 10:00 0:00 ps aux
# The host's processes are invisible
echo $$
# 1
The --fork flag is required because the PID namespace only takes effect for child processes. --mount-proc remounts /proc so that ps shows the correct PID view.
Hands-On: Network Namespace
Network namespaces provide isolated network stacks, each with its own interfaces, routing tables, and port space:
# Create a named network namespace
sudo ip netns add mycontainer
# List network namespaces
ip netns list
# Run a command inside the namespace
sudo ip netns exec mycontainer ip addr
# Only a loopback interface exists, and it's DOWN
# Bring up the loopback
sudo ip netns exec mycontainer ip link set lo up
To connect a network namespace to the host, use a veth pair (virtual ethernet):
# Create a veth pair
sudo ip link add veth-host type veth peer name veth-container
# Move one end into the namespace
sudo ip link set veth-container netns mycontainer
# Configure IP addresses
sudo ip addr add 10.0.0.1/24 dev veth-host
sudo ip link set veth-host up
sudo ip netns exec mycontainer ip addr add 10.0.0.2/24 dev veth-container
sudo ip netns exec mycontainer ip link set veth-container up
# Test connectivity
sudo ip netns exec mycontainer ping 10.0.0.1
ping 10.0.0.2
This is exactly how Docker creates networking for containers — each container gets a veth pair connecting it to a bridge on the host.
Hands-On: Mount Namespace
Mount namespaces isolate the filesystem view. Changes to mounts in one namespace are invisible in another:
# Create a new mount namespace
sudo unshare --mount /bin/bash
# Create a tmpfs mount that only exists in this namespace
mkdir -p /mnt/secret
mount -t tmpfs tmpfs /mnt/secret
echo "namespace-only data" > /mnt/secret/data.txt
# In another terminal, /mnt/secret is empty or doesn't exist
ls /mnt/secret/
# (empty or error)
Hands-On: User Namespace
User namespaces allow a process to have root privileges inside the namespace while being an unprivileged user on the host. This is the foundation of rootless containers:
# Create a user namespace (no sudo needed!)
unshare --user --map-root-user /bin/bash
# Inside the namespace, you appear to be root
whoami
# root
id
# uid=0(root) gid=0(root)
# But on the host, you're still your regular user
# This "root" cannot actually modify host resources
How Docker Uses Namespaces
When you run docker run -it ubuntu bash, Docker creates a process with all these namespaces:
# See the namespaces of a Docker container
docker inspect --format '{{.State.Pid}}' <container_id>
# Returns the PID on the host, e.g., 5678
ls -la /proc/5678/ns/
# Each namespace has a unique inode, different from the host
A Docker container typically uses:
- PID namespace — Container sees only its own processes, PID 1 is the entrypoint
- Network namespace — Container has its own eth0, IP address, and port space
- Mount namespace — Container sees the image’s filesystem, not the host’s
- UTS namespace — Container has its own hostname (the container ID)
- IPC namespace — Isolated shared memory and message queues
- User namespace — (Optional) Maps container root to unprivileged host user
Entering Existing Namespaces with nsenter
nsenter lets you enter the namespaces of a running process. This is invaluable for debugging containers:
# Enter all namespaces of a container process
sudo nsenter -t <PID> --all
# Enter just the network namespace
sudo nsenter -t <PID> --net
# Debug a container's network without exec
CONTAINER_PID=$(docker inspect --format '{{.State.Pid}}' mycontainer)
sudo nsenter -t $CONTAINER_PID --net ip addr
sudo nsenter -t $CONTAINER_PID --net ss -tlnp
sudo nsenter -t $CONTAINER_PID --net curl localhost:8080
Building a Minimal Container
Combining namespaces, we can build a rudimentary container from scratch:
# Create an isolated environment with multiple namespaces
sudo unshare --pid --mount --uts --ipc --net --fork /bin/bash
# Set the hostname
hostname my-container
# Mount a fresh proc
mount -t proc proc /proc
# Now we have:
# - Our own PID space (we are PID 1)
# - Our own hostname
# - Our own network stack (empty, no interfaces)
# - Our own mount table
# - Our own IPC resources
This is essentially what container runtimes like runc do, plus adding cgroups for resource limits and a root filesystem from an image.
Namespace Persistence
Namespaces normally disappear when the last process inside them exits. To keep a namespace alive without a running process, you can bind-mount it:
# Create a persistent network namespace
sudo ip netns add persistent-ns
# This creates /var/run/netns/persistent-ns as a bind mount
# Or manually bind-mount any namespace
sudo touch /var/run/ns/my-pid-ns
sudo mount --bind /proc/$$/ns/pid /var/run/ns/my-pid-ns
Summary
Linux namespaces are the isolation technology that makes containers possible. Each namespace type isolates a different system resource: PID for process IDs, network for network stacks, mount for filesystems, UTS for hostnames, user for UID/GID mappings, and more. You can create and explore namespaces with unshare and enter existing ones with nsenter. Understanding namespaces demystifies what Docker and Kubernetes are actually doing under the hood — they are not virtual machines, but clever combinations of kernel namespace and cgroup features.
Related articles
- Linux Linux Network Namespaces Tutorial
Hands-on guide to Linux network namespaces. Create isolated network stacks, connect them with veth pairs, and understand how containers get networking.
- Linux cgroups for Resource Isolation and Limits on Linux
Learn how to use Linux cgroups to limit CPU, memory, and I/O for processes, and understand their role in containers.
- Linux Linux cgroups Explained: How Containers Get Their Limits
A practical introduction to Linux control groups. Learn what cgroups do, how v1 and v2 differ, and how Docker and Kubernetes use them to cap CPU and memory.
- Linux Linux awk and sed: Text Processing Power Tools
Master awk and sed for text processing on Linux. Learn pattern matching, field extraction, in-place editing, and real-world one-liners for log analysis.