An init process can be defined as the top-most process started by the kernel at the system boot. They are responsible for starting the rest of the services on the system. These services include the SSH daemon, Nginx/Apache e.t.c. Each of the started processes may further give rise to child processes. A Zombie is a child process that terminates but has not waited for. The kernel maintains information about the Zombie. This information can be, PID, resource usage information, or termination status. These Zombies are harmful and should be removed via a wait. They consume slots in the kernel process table which fills and prevents the creation of further processes.
Tini is a tiny and simplest init available for containers. It works by spawning a single child and waiting for it to exit while reaping the zombie processes as well as performing signal forwarding.
It is mainly preferred due to the following benefits.
- Protects the kernel from software that accidentally creates zombie processes that starve the system PIDs
- Transparent and can be used on any container that works without Tini with no changes required.
- It ensures that the default signal handlers work for the software you run in your Docker image. For example, using SIGTERM terminates the process even if it did not install a signal handler for it.
This guide provides the required knowledge on how to use the Tini Init system in Docker Containers.
Add Tini to your Container.
Tini exists in Docker 1.13 and greater versions. It can easily be enabled using the
--init flag while running the docker container.
Using Prebuilt Tini Images
There are also prebuilt images for Tini for Ubuntu and CentOS base which can as well be used as a drop-in replacement of your base. All you do is modify your Dockerfile to use the Tini images instead.
# Change this: FROM ubuntu:trusty # To this: FROM krallin/ubuntu-tini:trusty
With this image, the ENTRYPOINT shipped should point to Tini. If you already have an ENTRYPOINT configured, modify it as below.
# Change this: ENTRYPOINT ["/docker-entrypoint.sh"] # To this: ENTRYPOINT ["/usr/local/bin/tini", "--", "/docker-entrypoint.sh"]
Remember if you aren’t using an ENTRYPOINT, don’t make the above changes.
Using Tini Packages
There are Tini packages available for Alpine Linux and NixOS and can be added to your Docker file as below.
Alpine Linux Package
RUN apk add --no-cache tini # Tini is now available at /sbin/tini ENTRYPOINT ["/sbin/tini", "--"]
nix-env --install tini
Manually add Tini
You can also add Tini to your container and make it executable. Proceed and invoke Tini and pass the program and arguments.
# Add Tini ENV TINI_VERSION v0.19.0 ADD https://github.com/krallin/tini/releases/download/$TINI_VERSION/tini /tini RUN chmod +x /tini ENTRYPOINT ["/tini", "--"] # Run your program under Tini CMD ["/your/program", "-and", "-its", "arguments"] # or docker run your-image /your/program ...
Alternatively, you can use signed binaries when adding Tini to your container above.
# Add Tini ENV TINI_VERSION v0.19.0 ADD https://github.com/krallin/tini/releases/download/$TINI_VERSION/tini /tini ADD https://github.com/krallin/tini/releases/download/$TINI_VERSION/tini.asc /tini.asc RUN gpg --batch --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7 \ && gpg --batch --verify /tini.asc /tini RUN chmod +x /tini ENTRYPOINT ["/tini", "--"] # Run your program under Tini CMD ["/your/program", "-and", "-its", "arguments"] # or docker run your-image /your/program ...
To use the above command, ensure
gpg is installed from your package manager.
How to Use Tini Init system in Docker Containers
Here I will demonstrate how to use Tini from Docker itself by using the
A container run without passing the init flag sets the CMD as PID 1. In this case, the CMD is /bin/bash.
$ docker run -ti --rm ubuntu:20.04 /bin/bash Unable to find image 'ubuntu:20.04' locally 20.04: Pulling from library/ubuntu d5fd17ec1767: Pull complete Digest: sha256:47f14534bda344d9fe6ffd6effb95eefe579f4be0d508b7445cf77f61a0e5724 Status: Downloaded newer image for ubuntu:20.04 [email protected]:/#
Check the PIDs.
[email protected]:/# ps -fA UID PID PPID C STIME TTY TIME CMD root 1 0 0 09:19 pts/0 00:00:00 /bin/bash root 8 1 0 09:20 pts/0 00:00:00 ps -fA
The output above shows the CMD as PID 1. Now let’s see what happens when the –init flag is invoked.
$ docker run -ti --init --rm ubuntu:20.04 /bin/bash [email protected]:/# ps -fA UID PID PPID C STIME TTY TIME CMD root 1 0 0 09:22 pts/0 00:00:00 /sbin/docker-init -- /bin/ba root 6 1 0 09:22 pts/0 00:00:00 /bin/bash root 9 6 0 09:22 pts/0 00:00:00 ps -fA
When Tini init is used, it becomes PID 1 as shown above. Now from here, Tini with PID 1 will reap forked child processes correctly.
Another example using the prebuilt Tini images:
$ docker run -ti krallin/ubuntu-tini:trusty /bin/bash [email protected]:/# ps -fA UID PID PPID C STIME TTY TIME CMD root 1 0 0 09:39 pts/0 00:00:00 /usr/bin/tini -- /bin/bash root 6 1 0 09:39 pts/0 00:00:00 /bin/bash root 16 6 0 09:39 pts/0 00:00:00 ps -fA
You can as well execute these commands to a running container with Tini as below.
To check the PIDs.
docker exec -it
Check the Tini version.
$ docker exec -it semaphore tini --version tini version 0.19.0
For Tini to reap zombies, it should run as PID 1. This ensures that zombies are re-parented to Tini. If Tini cannot run as PID 1, you need to register Tini as a process subreaper. This can be done in two ways:
- Passing the
-sargument to Tini (
tini -s -- …)
- invoke the variable
Remapping exit codes
Tini reuses the child’s exit code when exiting. This may not be exactly what you prefer. For example, if a child process exits with 143 after receiving SIGTERM, This issue is notable in Java apps. You can use the
-e flag to remap the code to 0.
tini -e 143 -- ...
There are several options when using Tini, get help as below:
[email protected]:/# tini -h tini (tini version 0.19.0 - git.fec3683) Usage: tini [OPTIONS] PROGRAM -- [ARGS] | --version Execute a program under the supervision of a valid init process (tini) Command line options: --version: Show version and exit. -h: Show this help message and exit. -s: Register as a process subreaper (requires Linux >= 3.4). -p SIGNAL: Trigger SIGNAL when parent dies, e.g. "-p SIGKILL". -v: Generate more verbose output. Repeat up to 3 times. -w: Print a warning when processes are getting reaped. -g: Send signals to the child's process group. -e EXIT_CODE: Remap EXIT_CODE (from 0 to 255) to 0. -l: Show license and exit. Environment variables: TINI_SUBREAPER: Register as a process subreaper (requires Linux >= 3.4). TINI_VERBOSITY: Set the verbosity level (default: 1). TINI_KILL_PROCESS_GROUP: Send signals to the child's process group.
That marks the end of this guide on how to use the Tini Init system in Docker Containers. You can now use Tini to reap the zombie processes as well as perform signal forwarding. I hope this was significant to you.