Update: The workaround I posted above doesn’t forward signals correctly (see documentation of --fork
in unshare(1)), so it doesn’t support graceful shutdown. Here is a fancier version that forwards signals.
init-wrapper:
#!/bin/sh
# run /init with PID 1, creating a new PID namespace if necessary
if [ "$$" -eq 1 ]; then
# we already have PID 1
exec /init "$@"
else
# create a new PID namespace
exec unshare --pid sh -c '
# set up /proc and start the real init in the background
unshare --mount-proc /init "$@" &
child="$!"
# forward signals to the real init
trap "kill -INT \$child" INT
trap "kill -TERM \$child" TERM
# wait until the real init exits
# ("wait" returns early on signals; "kill -0" checks if the process exists)
until wait "$child" || ! kill -0 "$child" 2>/dev/null; do :; done
' sh "$@"
fi
Dockerfile:
...
COPY init-wrapper /
ENTRYPOINT ["/init-wrapper"]