multirun

A minimalist init process designed for Docker

View on GitHub

multirun

Build

A simple Unix utility in C to run multiple commands concurrently.

Usage: multirun "command1" "command2" ...

You can also add the -v option to get a full log of the processes it starts and kills.

Unlike most process managers multirun never attempts to restart one of its children if it crashes. Instead it will kill all its other children before exiting with an error code. This behavior is ideal when you just want to delegate the restart duty to the upper level, as example using systemd or Docker restart policies.

Installation

Package manager

Currently, multirun is only present in Alpine’s distribution

Alpine

apk add multirun

Binary install

The release page lists all provided binaries.

Here is an example install script for an x86_64 Linux using GNU libc. Replace the URL with another one if you use another system.

wget -c https://github.com/nicolas-van/multirun/releases/download/1.1.3/multirun-x86_64-linux-gnu-1.1.3.tar.gz -O - | tar -xz && \
mv multirun /bin

We provide binaries for the following systems:

From sources

This project necessitates CMake.

wget -c https://github.com/nicolas-van/multirun/archive/refs/tags/1.1.3.tar.gz -O - | tar -xz && \
mv multirun-1.1.3 multirun-src && \
cd multirun-src && \
cmake -S . -B build && \
cmake --build build && \
cp build/multirun /bin && \
cd .. && \
rm -rf multirun-src

Technical Description of Behavior

FAQ

When to use multirun in a Docker container

The Docker documentation and most best practices documents regarding Docker tells that you should embed no more than one application per container. This is indeed perfectly true.

One container is meant to contain one application. If you want to deploy multiple applications at once (like a web server and a database) you should arrange your containers to properly communicate with the external world using network and volumes, make them easily configurable though environment variables and pack everything in a docker-compose or similar tool configuration. That is the correct way to do the job 95% of the time. It will allow you to keep each application separate from each other, to isolate their respective environments, to manage their lifecycle separately, to identify clearly which volume is dedicated to what, to scale all applications independently, etc…

Then there is the remaining 5% where it’s hard to define precisely what an application is. An application is not just a single process (countless web servers and databases spawn multiple processes for performance reason) nor a number of processes doing the same thing (again, a lot of applications spawn different processes with different purposes). Multirun, or any process manager in general, should be considered in a Docker container only for those specific cases where you consider that a single application necessitate multiple processes to fulfill its single purpose.

Here are some good use cases where multirun can be useful:

Here is an example of bad use case:

My processes do not exit correctly

A common cause for these problems is invalid usage of shell scripts that causes signals to not be propagated properly.

If you call multirun in a shell script, check that you launch it with the exec sh command. Example:

#!/bin/sh

# any init code

exec multirun arg1 arg2

If you don’t use exec your main process will likely remain /bin/sh which won’t forward signals correctly to your multirun process.

This advice is not specific to multirun and does apply to most containers that have a shell script as entrypoint.

Also, if you launch scripts with multirun that will launch the service you want, it is recommended to add exec in these scripts as well. Example:

# multirun call
multirun "./service1.sh" "./service2.sh"
# ./service1.sh
#!/bin/sh

# any init code

exec service1_executable
# ./service2.sh
#!/bin/sh

# any init code

exec service2_executable

If it still doesn’t work launch multirun in verbose mode and try to understand what’s going on. Your problem is probably caused by a child process that doesn’t exit properly or not fast enough when it receives a signal.

Contributing

See the contribution guide.

License

See the license.