In this blog post, I’m going to look at some init systems, but not very thoroughly:
What Makes it a “Container” Init System?
A tool can be considered “container” (PID1) ready if it can do two things:
- Handle signals as PID1: In Linux, any PID1 process won’t automatically get signal handlers it hasn’t registered for.
If you don’t do this, your process will not respond to signals as expected, and you will have long and non-graceful shutdown time.
- Zombie process reaping:
Most programs are not ready to
wait()
on random forked processes, but this is a PID1 responsibility.
If you don’t do this, forked processes end up piling up in your container and fill up the Linux process table.
What? I thought Containers Didn’t Need An Init System?
Even if you run a “single process container”, you probably need something to be PID1 for the above two reasons.
You might also want something fancier if you want process supervision.
You almost certainly want something fancier if you are running multiple things in the same container.
You Are Wrong! I Only Run (one thing)
in a Container and its Fine!
In 2025, quite a few apps have added patches to behave better in a container as PID1.
If you are using a k8s pod with shareProcessNamespace enabled (not default), then you are also covered because the k8s pause
process handles signals and child reaping.
Here are a few industry examples I found:
- the official k8s nginx ingress container uses
dumb-init
. kube-state-metrics
has special reaper code in golang.- ElasticSearch uses tini.
- MongoDB doesn’t seem to do anything?.
- The official Docker docks also recommend
tini
.
So yea. By now in 2025, your app may be PID1-ready already.
Crash-only Software Versus Supervision
The next thing you should consider when evaluating init systems is process supervision.
This is another thing that k8s has implemented for you with container restart policies.
But sometimes the cost of crashing and restarting something is high.
There is a reason we don’t just do apache || reboot
.
Sometimes doing process supervision in a container is the right tool for the job. I’ll leave it as an exercise to the reader to figure out if they need it.
Comparison
Feature Chart
Tool | Supervision | Multi Process | Configuration | AKA |
---|---|---|---|---|
tini | ✗ | ✗ | CLI | docker’s --init |
dumb-init | ✗ | ✗ | CLI | |
pid1 | ✗ | ✗ | CLI | |
daemontools | ✓ | ✓ | /service style dirs |
svscanboot |
nitro | ✓ | ✓ | /service style dirs |
|
runit | ✓ | ✓ | /service style dirs |
runsvdir |
s6 | ✓ | ✓ | /service style dirs |
s6-svscan |
supervisord | ✓ | ✓ | INI Files | |
systemd | ✓ | ✓ | Unit files |
There are a couple of families:
-
The “pid1 wrappers”:
tini
,dumb-init
, andpid1
. The are specifically targeting containers and only solve the PID1 problems. -
The “daemontools”-inspired:
daemontools
,nitro
,runit
, ands6
. These use the “directory for each service” paradigm. -
The “application healthcheckers”?:
supervisord
but also tools like monit or god. These focus on more “application” than system process supervision? It is a blurry line, but nobody ever intended for these tools to monitorsshd
, contrast to the daemontools family. -
systemd
: It is in a class of its own. It is concerned with everything on a system. System service supervision is just one of its many functions.
Opinions
I really wish we didn’t need the pid1 wrappers at all. On all the container platforms I’ve built, I’ve made sure that none of our users have to worry about that. In other words, I strongly believe this is a platform problem, not something to be solved by apps.
These daemontools tools I think will slowly go out of style in the container world and be replaced by k8s pods and restart policies.
Likewise with supervisord
and similar tools, k8s as a product has a full suite of probes to pick from to make sure that the app stays healthy, and when it isn’t, the control-plane can take action.
The control-plane connection is what will make these “userspace” healthcheck tools slowly fade.
As for systemd
, if you are running systemd in your containers, more power to you! :)
I do think there was a missed opportunity for systemd
to have a container-native systemd-lite
thingy, that could be a pid1-wrapper and supervise a single unit.
Conclusion
At this point, after working with containers for a long time, the industry has kinda converged on the pod concept for running processes together in supervised way.
This does obsolete a lot of the responsibility of classic init/supervision helpers.
It does not obsolete the need for the process to behave like a proper PID1.
I wish that k8s pause
would have solved that problem once and for all for everyone, but in the end the use of shared pid namespaces by default was reverted in k8s 1.8, so now every container entrypoint is PID1.
I hope that over time, more language runtimes become “PID1-aware”, obsoleting the pid1 wrappers.
Till then, I just recommend tini
.
Comment via email