The key thing to understand: **you can't kill a zombie process** — it's already dead. A zombie is just a leftover entry in the process table holding the exit status, waiting for its parent to call `wait()`. There's no running code to signal, so SIGTERM, SIGKILL, and everything else are silently ignored.
The real problem is the **parent process** that isn't reaping its children.
## Diagnose
Find the zombies and, critically, their parent PIDs:
```bash
ps -eo pid,ppid,state,cmd | awk '$3 ~ /^Z/'
```
Or:
```bash
ps aux | grep -w Z
```
The `PPID` column is what you actually care about.
## Fix it
**1. Nudge the parent to reap.** Sending SIGCHLD sometimes prods a poorly written parent into calling `wait()`:
```bash
kill -CHLD <PPID>
```
This often does nothing, but it's free and non-destructive, so try it first.
**2. Kill or restart the parent.** When the parent dies, zombies are re-parented to `init`/`systemd` (PID 1), which reaps them immediately:
```bash
kill -TERM <PPID> # graceful
kill -KILL <PPID> # if it ignores TERM
```
If it's a service, prefer the proper restart so you don't leave things in a weird state:
```bash
systemctl restart <service>
```
**3. If the parent is PID 1** — that's a systemd bug or a broken container init. In a container, this usually means your entrypoint is a shell script or app that doesn't reap. Fix it with a real init: `docker run --init`, or use `tini`/`dumb-init` as PID 1.
## When to just ignore them
A handful of zombies consume no CPU and no memory — only a PID slot. If it's three zombies on a workstation, genuinely leave them alone; they'll disappear when the parent exits. Only act when they're accumulating toward `/proc/sys/kernel/pid_max`, which will eventually cause `fork()` failures.
Check the trend:
```bash
watch -n5 'ps -eo state | grep -c Z'
```
## Root cause
If they keep coming back, the parent has a bug. Proper fixes in the parent's code:
- Install a SIGCHLD handler that loops `waitpid(-1, &status, WNOHANG)` until it returns ≤ 0
- Or explicitly ignore SIGCHLD (`signal(SIGCHLD, SIG_IGN)`) so the kernel auto-reaps
- In Python, call `.wait()` or `.poll()` on your `subprocess.Popen` objects; in shell scripts, `wait` on background jobs
One note: rebooting will definitely clear them, but it's almost never necessary and masks the actual bug. Killing the parent gets you the same result without the downtime.