How to fix zombie process detected in container in Docker

DockerINTERMEDIATEMEDIUMUpdated Dec 6, 2025ยท 344 views

This error occurs when orphaned child processes in a Docker container are not properly reaped, leading to zombie processes that accumulate over time. The fix involves using an init system like tini or Docker's --init flag to handle process reaping as PID 1.

Quick fix

Fixes zombie process detected in container

# Check for zombie processes inside the container
docker exec <container_name> ps aux | grep -E 'Z|defunct'

# Or use docker top to see all processes
docker top <container_name>
  1. Identify zombie processes in the container
  2. Use Docker's built-in --init flag
  3. Add tini to your Dockerfile
  4. Use dumb-init as an alternative
docker โ€” error
# Check for zombie processes inside the container
docker exec <container_name> ps aux | grep -E 'Z|defunct'
# Or use docker top to see all processes
docker top <container_name>
zombie process detected in container

What this error means

The "zombie process detected in container" error indicates that processes inside your Docker container are terminating but not being properly cleaned up. In Linux, when a process exits, it becomes a "zombie" until its parent process calls wait() to retrieve its exit status. Only then is it fully removed from the process table. In Docker containers, the main process runs as PID 1, which has special responsibilities in Linux. Unlike traditional init systems (like systemd), most applications are not designed to handle the responsibilities of PID 1, particularly reaping orphaned zombie processes and handling signals properly. When child processes exit and their parent doesn't call wait(), or when the parent exits before the child, the orphaned zombie gets reparented to PID 1. If PID 1 doesn't reap these zombies, they accumulate in the process table. While zombies don't consume CPU or memory, they do occupy slots in the kernel's process table, and over time this can exhaust available PIDs and prevent new processes from being created.

Typical symptoms

  • Processes show as <defunct> or Z state in ps output
  • Container's process count keeps growing over time
  • ps aux shows processes with STAT column containing 'Z'
  • Application logs mention zombie or defunct processes
  • Container eventually fails to spawn new processes
  • docker top shows accumulated defunct processes
  • System logs warn about process table exhaustion

Common causes

  1. Application running as PID 1 doesn't implement wait() for child processes
  2. Parent process exits before child processes complete
  3. Shell scripts spawning background processes without proper cleanup
  4. Applications forking child processes without signal handling
  5. No init system to adopt and reap orphaned processes
  6. docker exec spawned processes not being reaped (--init doesn't cover these)
  7. Multi-process applications without proper process management

How to fix "zombie process detected in container"

1Identify zombie processes in the container

First, confirm you have zombie processes by checking the process state:

bash
# Check for zombie processes inside the container
docker exec <container_name> ps aux | grep -E 'Z|defunct'

# Or use docker top to see all processes
docker top <container_name>

Zombie processes show:
- State 'Z' in the STAT column
- <defunct> in the command column
- They have a parent PID but are no longer running

To count zombies:

bash
docker exec <container_name> sh -c 'ps aux | grep -c defunct'

Check if zombies are accumulating over time by running this periodically.

2Use Docker's built-in --init flag

The simplest solution is to use Docker's built-in init process. Since Docker 1.13, the --init flag adds tini as PID 1:

bash
docker run --init <image>

This injects tini (a minimal init system) that:
- Runs as PID 1 in the container
- Reaps zombie processes by calling wait()
- Forwards signals to child processes properly

For Docker Compose, add init: true to your service:

yaml
services:
  myapp:
    image: myimage:latest
    init: true

Verify tini is running as PID 1:

bash
docker exec <container_name> ps aux | head -2

You should see /sbin/docker-init or tini as PID 1.

3Add tini to your Dockerfile

If you need the init system baked into your image (for environments where --init isn't available), add tini directly:

For Alpine-based images:

dockerfile
FROM alpine:3.18
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["your-application"]

For Debian/Ubuntu-based images:

dockerfile
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y tini && rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["your-application"]

Download tini binary directly:

dockerfile
FROM your-base-image
ENV TINI_VERSION v0.19.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]
CMD ["your-application"]

Important: Use the JSON array syntax for ENTRYPOINT and CMD to avoid shell wrapping.

4Use dumb-init as an alternative

dumb-init from Yelp is another lightweight init system option:

In your Dockerfile:

dockerfile
FROM python:3.11-slim
RUN apt-get update && apt-get install -y dumb-init && rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["python", "app.py"]

For Alpine:

dockerfile
FROM alpine:3.18
RUN apk add --no-cache dumb-init
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["./your-app"]

dumb-init provides:
- Signal forwarding to child processes
- Zombie reaping
- Process group management with --single-child flag

The behavior is very similar to tini. Choose based on your preference or what's easier to install in your base image.

5Fix application-level process management

If zombies are caused by your application not waiting for children, fix the code:

Python example - proper subprocess handling:

python
import subprocess
import signal
import os

# Reap children on SIGCHLD
def reap_children(signum, frame):
    while True:
        try:
            pid, status = os.waitpid(-1, os.WNOHANG)
            if pid == 0:
                break
        except ChildProcessError:
            break

signal.signal(signal.SIGCHLD, reap_children)

# Always wait for subprocesses
proc = subprocess.Popen(['command'])
proc.wait()  # Don't forget this!

Node.js example:

javascript
const { spawn } = require('child_process');

const child = spawn('command', ['args']);

child.on('exit', (code, signal) => {
  console.log(`Child exited with code ${code}`);
});

// Handle cleanup on exit
process.on('exit', () => {
  child.kill();
});

Shell script example:

bash
#!/bin/bash
# Use exec for the final command to replace the shell
exec your-application

# Or if spawning background processes, wait for them
background_task &
pid=$!
wait $pid
6Handle docker exec spawned zombies

Important limitation: docker run --init does NOT reap zombies from processes started via docker exec. These processes are managed separately.

If you frequently use docker exec and see zombies from those commands:

Option 1: Use tini with subreaper mode
Tini can register as a subreaper to adopt zombies even when not PID 1:

dockerfile
ENTRYPOINT ["/sbin/tini", "-s", "--"]

The -s flag enables subreaper mode.

Option 2: Wrap exec commands with an init

bash
docker exec <container> /sbin/tini -- your-command

Option 3: Use bash -c for simple commands
Bash has built-in zombie reaping when running as PID 1 of a process group:

bash
docker exec <container> bash -c 'your-command'

This helps for commands that spawn subprocesses.

7Monitor and prevent zombie accumulation

Set up monitoring to catch zombie issues early:

Check zombie count periodically:

bash
#!/bin/bash
ZOMBIES=$(docker exec mycontainer sh -c 'ps aux | grep -c defunct' 2>/dev/null || echo 0)
if [ "$ZOMBIES" -gt 10 ]; then
  echo "WARNING: $ZOMBIES zombie processes detected!"
fi

Add a healthcheck that monitors zombies:

dockerfile
HEALTHCHECK --interval=60s --timeout=10s --retries=3 \
  CMD [ "$(ps aux | grep -c defunct)" -lt 50 ] || exit 1

Docker Compose healthcheck:

yaml
services:
  myapp:
    image: myimage
    init: true
    healthcheck:
      test: ["CMD-SHELL", "[ $(ps aux | grep -c defunct) -lt 50 ]"]
      interval: 60s
      timeout: 10s
      retries: 3

For production, consider using Prometheus with a custom metric that exports zombie process count.

Advanced notes

Understanding PID 1 in Linux: PID 1 has special kernel treatment. It won't receive signals it hasn't explicitly registered handlers for (unlike other processes where unhandled signals use default behavior). This means SIGTERM won't kill PID 1 unless it handles SIGTERM. Init systems like tini register signal handlers and forward them to child processes.

The zombie lifecycle: A process becomes a zombie when it exits but its parent hasn't called wait(). The zombie exists so the parent can retrieve the exit status. When the parent exits, orphaned zombies are reparented to PID 1. If PID 1 doesn't reap them, they persist until the container stops.

Process table limits: While zombies don't use CPU or memory, they occupy a slot in the process table. Linux has a limit (typically 32768 by default, configurable via /proc/sys/kernel/pid_max). If zombies exhaust this limit, the system cannot create new processes.

Using s6-overlay for complex scenarios: For containers running multiple services, consider s6-overlay which provides:

dockerfile
FROM your-base
ADD https://github.com/just-containers/s6-overlay/releases/download/v3.1.5.0/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ENTRYPOINT ["/init"]

s6 handles zombie reaping, service supervision, and graceful shutdown of multiple processes.

Kubernetes considerations: In Kubernetes, you can enable shareProcessNamespace to allow containers to see each other's processes, but this doesn't help with zombie reaping. Always use an init system in your container image:

yaml
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: app
    image: myimage  # Image should include tini/dumb-init

Debugging zombie parent processes: To find what's creating zombies, check the PPID:

bash
docker exec container ps -eo pid,ppid,stat,cmd | grep Z

The PPID shows the parent. If PPID is 1, the original parent already exited and the zombie was reparented.

Frequently asked questions

What does "zombie process detected in container" mean?
The "zombie process detected in container" error indicates that processes inside your Docker container are terminating but not being properly cleaned up. In Linux, when a process exits, it becomes a "zombie" until its parent process calls wait() to retrieve its exit status. Only then is it fully removed from the process table.
How do I fix "zombie process detected in container"?
This error occurs when orphaned child processes in a Docker container are not properly reaped, leading to zombie processes that accumulate over time. The fix involves using an init system like tini or Docker's --init flag to handle process reaping as PID 1.
What causes "zombie process detected in container"?
Application running as PID 1 doesn't implement wait() for child processes Parent process exits before child processes complete Shell scripts spawning background processes without proper cleanup
Is the "zombie process detected in container" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading