A Docker container isn’t a virtual machine. It’s a set of ordinary Linux processes that the kernel shows a restricted view of the system: their own process list, network interfaces, hostname, and filesystem. That isolation comes from namespaces. Combined with cgroups (resource limits) and capabilities (privilege limits), namespaces are what make containers possible.

The namespace types

Namespace Isolates Effect inside the namespace
PID Process IDs The container sees its own processes, starting at PID 1
Network (net) Interfaces, IPs, routes, firewall rules, ports Its own eth0, its own port 80
Mount (mnt) Mount points Its own root filesystem and mounts
UTS Hostname and domain name Its own hostname
IPC Shared memory, message queues Separate IPC objects
User User and group IDs Root inside can map to an unprivileged user outside
Cgroup View of the cgroup hierarchy Sees its own cgroup as the root
Time Some system clocks Separate boot and monotonic clocks (rarely used)

See namespaces on your server

lsns                      # all namespaces and their processes
lsns -t net               # network namespaces only
ls -l /proc/$$/ns         # namespaces of your current shell
sudo ls -l /proc/$(docker inspect -f '{{.State.Pid}}' nginx)/ns    # a container's namespaces

Processes that share a namespace show the same inode number for it.

Try it: unshare

unshare runs a program in new namespaces:

# new UTS namespace: change the hostname without affecting the system
sudo unshare --uts bash -c 'hostname sandbox; hostname'
hostname     # unchanged

# new PID + mount namespace: ps only shows processes inside
sudo unshare --pid --fork --mount-proc bash
ps aux       # bash is PID 1
exit

Enter a namespace: nsenter

nsenter runs a command inside an existing process’s namespaces, which is useful for debugging containers that have no shell or tools:

PID=$(docker inspect -f '{{.State.Pid}}' web)
sudo nsenter -t "$PID" -n ip addr          # container's network view, with host tools
sudo nsenter -t "$PID" -n ss -tlnp         # what's listening inside
sudo nsenter -t "$PID" -m -u -i -n -p sh   # (almost) like docker exec

Network namespaces

Network namespaces give each container (or VPN or test environment) its own network stack. They’re also usable directly with ip netns. See Linux network namespaces.

User namespaces and rootless containers

With a user namespace, a process can be root (UID 0) inside, while being an unprivileged user (for example UID 100000) on the host. That’s the basis of rootless Docker and Podman: even if an attacker escapes the container, they don’t have host root. See Docker rootless mode.

unshare --user --map-root-user bash -c 'id'     # uid=0(root) — but only inside

Security implications

  • Namespaces provide isolation, not a hard security boundary like a hypervisor. Containers share the host kernel, so a kernel vulnerability can allow escapes. That’s why untrusted workloads are better isolated in separate VMs. See Docker vs virtual machine and what is KVM virtualization.
  • Avoid --privileged, --pid=host and --net=host unless needed. They remove isolation layers.
  • Sharing the host network namespace (--network host) also means container ports bypass Docker’s port mapping. See Docker and UFW.

Namespaces + cgroups + capabilities

Feature Controls
Namespaces What a process sees
cgroups How much it can use. See Linux cgroups
Capabilities Which privileged actions it may do. See Linux capabilities
seccomp, AppArmor/SELinux Which syscalls and resources it may access

Quick reference

Task Command
List namespaces lsns
A process’s namespaces ls -l /proc/<pid>/ns
Run in new namespaces unshare --uts --pid --fork …
Enter a container’s namespaces nsenter -t <pid> -n …
Network namespaces ip netns

Related: Docker Compose tutorial and Kubernetes security basics.

  • Linux
  • Containers
  • Security