Tools: Understanding PID Namespaces: The Small Linux Feature Behind Container Process Isolation (2026)

Tools: Understanding PID Namespaces: The Small Linux Feature Behind Container Process Isolation (2026)

Understanding PID Namespaces: The Small Linux Feature Behind Container Process Isolation

What problem does a PID namespace solve?

PID 1 is not just “the first process”

The senior-level lesson: containers are isolation, not virtualization

How Docker uses PID namespaces

What is the “hash” inside /proc/<pid>/ns/pid?

How to check PID namespace isolation

How PID namespace isolation can be weakened

1. Running with host PID namespace

2. Running privileged containers

3. Mounting sensitive host paths

4. Adding dangerous capabilities

5. Weak Kubernetes security context

Defensive checklist for real projects

Runtime

Capabilities

Privileged mode

Process tree

Namespace comparison

Kubernetes

A practical example from building a tiny container runtime

Senior engineering lessons

1. Do not confuse isolation with security

2. PID 1 behavior matters

3. Debugging containers requires two views

4. Most “container escapes” start with bad configuration

5. Use namespaces intentionally

References

Final thought When people first learn containers, they usually hear this sentence: “A container is just a process.” That sentence is true, but incomplete. “A container is a regular Linux process running with a different view of the system.” One of the most important parts of that different view is the PID namespace. A PID namespace controls what processes a process can see and what process IDs look like from inside that environment. It is one of the Linux kernel features that makes containers feel isolated, even though everything is still running on the same host kernel. Docker, containerd, runc, Kubernetes, and even small learning projects like a tiny Docker-like runtime all rely on this idea. On a normal Linux machine, every process has a PID: You may see things like: Without PID isolation, a process inside a container could see host processes. That would be noisy, confusing, and dangerous. With a PID namespace, the container gets its own process ID view. Inside the container: On the host, those same processes still have real host PIDs: So the same process can have two identities: This is not magic. It is namespace-based translation done by the Linux kernel. A very common beginner mistake is thinking PID 1 is only a number. Inside a PID namespace, the first process becomes PID 1, and PID 1 has special responsibilities. In a normal Linux system, PID 1 is usually systemd or another init system. In a container, PID 1 might be your application: If your app becomes PID 1 directly, it now behaves like the init process of that namespace. That matters because PID 1 is responsible for handling orphaned child processes and reaping zombies. The Linux man pages describe the first process in a new PID namespace as the namespace init process, and orphaned children in that namespace are reparented to it. This is why senior engineers often care about tiny init processes like: Without a proper init process, long-running containers can slowly accumulate zombie processes. A container may look healthy from the outside, but inside it can be leaking process table entries because PID 1 is not doing its job. A VM gets its own kernel. A container does not. A container shares the host kernel, but gets isolated views using kernel features like: The PID namespace only isolates process visibility and PID numbering. It does not magically secure everything. That is a critical mental model. A PID namespace can stop a container from seeing host processes, but it does not protect you from: This is why container security is usually about layers, not one feature. By default, Docker gives containers their own PID namespace. Docker exposes this through the --pid option. The default mode isolates processes, while --pid=host makes the container use the host PID namespace. Inside the container, you may see only a few processes. But with host PID mode: The container can see host processes. That flag is useful for debugging, monitoring, and observability tools, but it should be treated carefully. In production, --pid=host removes an important isolation boundary. When you inspect namespaces, you may see something like this: People sometimes casually call this a “namespace hash”, but it is not a cryptographic hash. It is a kernel namespace identifier exposed through procfs. Namespace references are shown as special symbolic links, and the number helps identify whether two processes are in the same namespace. If two processes show the same namespace ID for pid, they share the same PID namespace. If both return the same value, both processes are in the same PID namespace. This is very useful for debugging containers. From inside a container: If you only see the container’s own processes, PID isolation is probably enabled. Check the namespace ID: From the host, inspect a container process: You can compare namespace IDs between host processes and container processes. Another useful command: This shows PID namespaces on the system. For deeper debugging: The trick is to always remember that the host sees the full truth, while the container sees a translated view. This is where many real-world mistakes happen. PID namespaces are not usually “bypassed” by magic. They are usually weakened by configuration choices. Here are common examples. This makes the container see host processes. Sometimes this is used by monitoring tools, but it should not be the default for normal application containers. A privileged container receives broad access that removes many normal container restrictions. This is sometimes convenient during development, but it should be avoided for normal production workloads. Mounting the Docker socket is especially dangerous because it can effectively give control over the Docker daemon. Capabilities such as these should be reviewed carefully: For PID and process security, SYS_PTRACE is especially sensitive because it relates to inspecting and tracing processes. In Kubernetes, settings like these are important: For normal workloads, these should usually be avoided. When reviewing a containerized service, I usually ask these questions. Check whether the container is using host PID mode. Prefer dropping unnecessary capabilities: Then add back only what is truly required. For most application containers, this should be false. Look for zombie processes: If you see zombies, check whether PID 1 is properly reaping children. Compare host and container namespace IDs. These settings should be intentional, documented, and reviewed. When building a minimal Docker-like runtime, PID namespace support usually starts with something like: But there is a subtle detail. When you create a new PID namespace, the child process becomes PID 1 inside that namespace. The parent still lives in the old namespace. That means your runtime has to think carefully about: This is where the learning becomes real. Creating a namespace is easy. Managing a namespace correctly is the hard part. PID namespaces provide process isolation, but they are only one part of the security model. If your application runs as PID 1, signal handling and zombie reaping become your problem. The same process has different PIDs depending on where you look from. In real systems, the issue is often not the PID namespace itself. The issue is combining weak settings: For observability tools, hostPID or --pid=host may be required. For normal application workloads, it is usually unnecessary risk. PID namespaces are one of those Linux features that look simple at first: “The container gets its own process IDs.” But after working with real systems, you realize the deeper lesson: Process isolation is not only about hiding PIDs. It is about controlling visibility, lifecycle, signals, debugging, and failure boundaries. That is why PID namespaces are not just a container feature. They are a production engineering concept. If you understand PID namespaces well, Docker feels less like magic and more like a thin layer over powerful Linux primitives. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to ? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Code Block

Copy

ps aux PID 1 systemd PID 842 sshd PID 1201 nginx PID 2300 node PID 1 systemd PID 842 sshd PID 1201 nginx PID 2300 node PID 1 systemd PID 842 sshd PID 1201 nginx PID 2300 node PID 1 app PID 7 worker PID 12 shell PID 1 app PID 7 worker PID 12 shell PID 1 app PID 7 worker PID 12 shell PID 34520 app PID 34541 worker PID 34610 shell PID 34520 app PID 34541 worker PID 34610 shell PID 34520 app PID 34541 worker PID 34610 shell docker run my-api docker run my-api docker run my-api tini dumb-init tini dumb-init tini dumb-init docker run --rm -it ubuntu ps aux docker run --rm -it ubuntu ps aux docker run --rm -it ubuntu ps aux docker run --rm -it --pid=host ubuntu ps aux docker run --rm -it --pid=host ubuntu ps aux docker run --rm -it --pid=host ubuntu ps aux /proc/<pid>/ns/pid readlink /proc/$$/ns/pid readlink /proc/$$/ns/pid readlink /proc/$$/ns/pid pid:[4026531836] pid:[4026531836] pid:[4026531836] readlink /proc/1/ns/pid readlink /proc/$$/ns/pid readlink /proc/1/ns/pid readlink /proc/$$/ns/pid readlink /proc/1/ns/pid readlink /proc/$$/ns/pid ps aux readlink /proc/1/ns/pid readlink /proc/$$/ns/pid readlink /proc/1/ns/pid readlink /proc/$$/ns/pid readlink /proc/1/ns/pid readlink /proc/$$/ns/pid docker inspect --format '{{.State.Pid}}' <container_id> docker inspect --format '{{.State.Pid}}' <container_id> docker inspect --format '{{.State.Pid}}' <container_id> readlink /proc/<host_pid>/ns/pid readlink /proc/<host_pid>/ns/pid readlink /proc/<host_pid>/ns/pid lsns -t pid lsns -t pid lsns -t pid pstree -p ps -eo pid,ppid,cmd ps -eo pid,ppid,cmd ps -eo pid,ppid,cmd --pid=host --privileged --privileged --privileged -v /proc:/host/proc -v /:/host -v /var/run/docker.sock:/var/run/docker.sock -v /proc:/host/proc -v /:/host -v /var/run/docker.sock:/var/run/docker.sock -v /proc:/host/proc -v /:/host -v /var/run/docker.sock:/var/run/docker.sock SYS_ADMIN SYS_PTRACE NET_ADMIN DAC_READ_SEARCH SYS_ADMIN SYS_PTRACE NET_ADMIN DAC_READ_SEARCH SYS_ADMIN SYS_PTRACE NET_ADMIN DAC_READ_SEARCH hostPID: true privileged: true allowPrivilegeEscalation: true hostPID: true privileged: true allowPrivilegeEscalation: true hostPID: true privileged: true allowPrivilegeEscalation: true docker inspect <container_id> | grep -i pid docker inspect <container_id> | grep -i pid docker inspect <container_id> | grep -i pid docker inspect <container_id> | grep -i cap docker inspect <container_id> | grep -i cap docker inspect <container_id> | grep -i cap --cap-drop=ALL --cap-drop=ALL --cap-drop=ALL docker inspect <container_id> | grep -i privileged docker inspect <container_id> | grep -i privileged docker inspect <container_id> | grep -i privileged docker exec -it <container_id> ps aux docker exec -it <container_id> ps aux docker exec -it <container_id> ps aux ps aux | grep Z ps aux | grep Z ps aux | grep Z readlink /proc/1/ns/pid readlink /proc/$$/ns/pid readlink /proc/1/ns/pid readlink /proc/$$/ns/pid readlink /proc/1/ns/pid readlink /proc/$$/ns/pid hostPID: true securityContext: privileged: true allowPrivilegeEscalation: true hostPID: true securityContext: privileged: true allowPrivilegeEscalation: true hostPID: true securityContext: privileged: true allowPrivilegeEscalation: true SysProcAttr: &syscall.SysProcAttr{ Cloneflags: syscall.CLONE_NEWPID, } SysProcAttr: &syscall.SysProcAttr{ Cloneflags: syscall.CLONE_NEWPID, } SysProcAttr: &syscall.SysProcAttr{ Cloneflags: syscall.CLONE_NEWPID, } docker run --pid - one PID inside the container - another PID on the host - PID namespaces - mount namespaces - network namespaces - UTS namespaces - IPC namespaces - user namespaces - dangerous Linux capabilities - privileged containers - host filesystem mounts - exposed Docker socket - weak seccomp, AppArmor, or SELinux profiles - kernel vulnerabilities - bad Kubernetes security context settings - who becomes PID 1 - whether PID 1 launches the user command directly - whether you need a small init process - how signals are forwarded - how child processes are reaped - what happens when PID 1 exits - inside the container - from the host - privileged mode - host mounts - excessive capabilities - exposed Docker socket - Linux man-pages: PID namespaces - Linux Kernel Documentation: Namespaces - Docker documentation: docker run --pid - OWASP Docker Security Cheat Sheet