PID 1 and init
PID 1 is the root of every process. It cannot be killed with SIGKILL — if it dies, the kernel panics. Its jobs: orphan reaping, service supervision, bringing the system to a target state. systemd replaced SysV init by making dependencies explicit and parallel.
You run kill -9 1 on a Linux box. Nothing happens. You try again. Still nothing. That is not a permissions bug — PID 1 is immune to SIGKILL by design. The kernel guarantees it: if PID 1 dies, there is no recovery path, so the kernel simply refuses to deliver the signal. Understanding why PID 1 is special — and what it actually does all day — demystifies everything from zombie processes to why your service did not restart after a crash.
After this lesson you can explain what PID 1 does and why it is special, describe how systemd differs from SysV init in startup model and dependency expression, understand orphan reaping, and read systemctl status output to understand the process tree.
PID 1 is the ancestor of every process on the system. When the kernel finishes its own initialization and executes /sbin/init, the resulting process gets PID 1. Every other process is either a direct child of PID 1 or a descendant. The process tree is rooted here. You can visualize it:
# Show the full process tree rooted at PID 1
pstree -p 1
# See PID 1's identity
ps -p 1 -o pid,comm,argsOn modern Debian/Ubuntu: 1 systemd /lib/systemd/systemd. On older systems or containers with a minimal init: 1 init /sbin/init or even 1 bash (if started with init=/bin/bash).
PID 1 reaps orphaned zombie processes — and if it does not, memory leaks. When any process exits, the kernel keeps a small record of it (the exit status) until the parent calls wait() to collect it. This record is called a zombie. Normally the parent collects it promptly. But if the parent dies before the child, the child becomes an orphan and is re-parented to PID 1. PID 1 is then responsible for calling wait() on it.
If PID 1 does not call wait() — because it is a naive shell or a poorly written init — orphans accumulate as zombies. Eventually the PID table fills and no new processes can be created. This is a real failure mode in Docker containers that use a custom CMD as PID 1 without proper signal forwarding and reaping.
# Count zombie processes — non-zero means PID 1 is not reaping
ps aux | awk '$8=="Z"' | wc -lPID 1 is immune to SIGKILL — by kernel design, not by privilege. Normally SIGKILL (signal 9) cannot be caught, blocked, or ignored by any process. It is the unconditional termination signal. The one exception: the kernel never delivers SIGKILL to PID 1. The rationale is simple — if PID 1 dies, the kernel has no way to continue; it would panic anyway. So the kernel protects PID 1 preemptively.
# This does nothing (even as root)
sudo kill -9 1
# PID 1 can still receive SIGTERM (and systemd handles it gracefully)
sudo kill -15 1 # systemd interprets this as "start kbrequest.target"Containers are a notable exception: inside a Docker container, PID 1 can be killed with SIGKILL from the host (via docker kill), because the host kernel treats the container’s init as a regular process from its perspective.
SysV init: sequential scripts, no dependency model. The predecessor to systemd was SysV init. Its model: numbered runlevels (0–6), and for each runlevel a set of shell scripts in /etc/rc<N>.d/ that started or stopped services. Scripts ran sequentially — one after another — which meant a 60-second boot even when most services had no dependency on each other. Dependencies were implicit: script ordering was the only coordination mechanism. A failed service could block everything after it.
# SysV-style service control (still works as compatibility shim on Ubuntu)
sudo service nginx start
sudo service nginx statussystemd: parallel activation with explicit dependency graph. systemd replaced SysV init by modeling everything as units with declared dependencies. When multi-user.target is requested, systemd walks the dependency graph and starts all units whose dependencies are satisfied in parallel. A service that only needs the network can start at the same time as a service that only needs a mount — they do not block each other.
# See what systemd activated at last boot and when
systemd-analyze plot > boot.svg # generates a visual timeline
# Inspect the dependency graph for a unit
systemctl list-dependencies sshd.service
# Check if PID 1 (systemd) is healthy
systemctl is-system-runningThe critical insight: systemd turned implicit ordering (script numbers) into an explicit graph (After=, Requires=, Wants=). This is why systemd boots are faster and why a failed optional service does not stall the rest of the boot.
Debug a zombie accumulation problem in a container.
A long-running Docker container started accumulating zombie processes (Z state in ps). The container used a Node.js app directly as CMD — so the Node process was PID 1. Node does not call wait() on unexpected children from signal handlers or subprocesses spawned by native add-ons.
Diagnosis:
# Inside the container, check for zombies
ps aux | awk '$8=="Z"'
# See the parent of the zombie (should be PID 1)
ps -eo pid,ppid,stat,comm | grep ZFix options:
# Option 1: use tini as PID 1 (a minimal init that only reaps)
# In Dockerfile:
# ENTRYPOINT ["/usr/bin/tini", "--"]
# CMD ["node", "server.js"]
# Option 2: Docker's built-in init
docker run --init myimage node server.jsThe lesson: any process used as PID 1 in a container must implement orphan reaping. A real init (systemd, tini, s6) does this automatically. Application processes typically do not.
▸Why this works
macOS uses launchd as PID 1 — it is both the init system and the service manager, equivalent to combining systemd’s init + socket activation + service supervision in one daemon. There is no SysV init on macOS. The zombie-reaping responsibility is the same: launchd reaps orphans. The conceptual model (PID 1 as process tree root, orphan adoption, service supervision) is universal across Unix-like systems.
▸Common mistake
A common misconception: “systemd is bloated and does too much.” The practical counterpoint is that SysV init did the same things — service management, device event handling, getty spawning — but spread across hundreds of shell scripts with no dependency model and no restart logic. systemd consolidated them behind a consistent interface (systemctl, unit files, journalctl). Whether you like the design, understanding its model makes you faster at every operational task.
A container runs a Python web server directly as PID 1. After a few hours, new requests hang and ps shows dozens of Z-state processes. What is the most likely cause?
PID 1 is the root of the process tree, executed by the kernel after initramfs pivots to the real root. It has two special kernel guarantees: it is the parent of all orphaned processes (which it must wait() on to prevent zombie accumulation), and it is immune to SIGKILL (if it died, the kernel would panic). SysV init ran numbered shell scripts sequentially with implicit ordering. systemd replaced it with an explicit dependency graph enabling parallel activation — faster boots and no cascading stalls. In containers, any process used as PID 1 must implement orphan reaping; use tini or --init if your app does not.
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.