How to reap zombie processes in Docker containers
tldr: Use tini to handle zombie processes in Docker containers.
I have a containerized Python FastAPI service that spawns child Chrome processes to fetch web content. I deployed it on my VPS and used it for quite some time. One day, when I logged into my machine, I saw the following:
Welcome to Ubuntu 22.04.5 LTS
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
=> There are 16 zombie processes.
It was my first time seeing this message. When I looked at the running processes, I saw some processes had the <defunct> at the end:
username@hostname:~# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 2245731 2243994 0 21:16 ? 00:00:00 [chrome_crashpad] <defunct>
root 2245733 2243994 0 21:16 ? 00:00:00 [chrome_crashpad] <defunct>
root 2245736 2243994 0 21:16 ? 00:00:00 [chrome] <defunct>
root 2245737 2243994 0 21:16 ? 00:00:00 [chrome] <defunct>
A defunct process is a process that has completed its task but still has an entry in the processes table. It’s the parent process’s responsibility to read the child process’s exit status and clear the process table. However, in my case, the uvicorn process in my Docker container doesn’t automatically reap orphaned processes.
Here is my Dockerfile for reference, at which the unicorn is the PID 1 of my image:
# Runtime stage
FROM python:3.14-slim@sha256:c845af9399020c7e562969a13689e929074a10fd057acd1b1fad06a2fb068e97
# # Install runtime dependencies only
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
wget \
libxml2 \
libxslt1.1 \
zlib1g \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
# Copy virtual environment from builder
...
# Copy application code
COPY app /app
ENV PYTHONPATH=/app
EXPOSE 8000
# uvicorn is the PID 1
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
To solve this orphan processes issue, I found tini which reap orphan processes automatically, and I just need to modify two lines of my Dockerfile:
FROM python:3.14-slim@sha256:c845af9399020c7e562969a13689e929074a10fd057acd1b1fad06a2fb068e97
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
wget \
libxml2 \
libxslt1.1 \
zlib1g \
tini \ # install tini
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
COPY app /app
ENV PYTHONPATH=/app
EXPOSE 8000
# tini is the new PID 1
ENTRYPOINT ["tini", "--"] # add a new entrypoint
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
With tini installed, I no longer see zombie processes.
As a side note, tini is included in Docker version 1.13+. So, without touching the Dockerfile, I can achieve the same thing by either passing the --init flag in Docker run, or in Docker Compose:
services:
myservice:
image: alpine:latest
init: true # add this, source: https://docs.docker.com/reference/compose-file/services/#init