Home

Awesome

Table of Contents

s6-overlay Build Status

s6-overlay is an easy-to-install (just extract a tarball or two!) set of scripts and utilities allowing you to use existing Docker images while using s6 as a pid 1 for your container and process supervisor for your services.

Quickstart

Build the following Dockerfile and try it out:

# Use your favorite image
FROM ubuntu
ARG S6_OVERLAY_VERSION=3.2.0.3

RUN apt-get update && apt-get install -y nginx xz-utils
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
CMD ["/usr/sbin/nginx"]

ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz
ENTRYPOINT ["/init"]
docker-host $ docker build -t demo .
docker-host $ docker run --name s6demo -d -p 80:80 demo
docker-host $ docker top s6demo acxf
PID                 TTY                 STAT                TIME                COMMAND
11735               ?                   Ss                  0:00                \_ s6-svscan
11772               ?                   S                   0:00                \_ s6-supervise
11773               ?                   Ss                  0:00                | \_ s6-linux-init-s
11771               ?                   Ss                  0:00                \_ rc.init
11812               ?                   S                   0:00                | \_ nginx
11814               ?                   S                   0:00                | \_ nginx
11816               ?                   S                   0:00                | \_ nginx
11813               ?                   S                   0:00                | \_ nginx
11815               ?                   S                   0:00                | \_ nginx
11779               ?                   S                   0:00                \_ s6-supervise
11785               ?                   Ss                  0:00                | \_ s6-ipcserverd
11778               ?                   S                   0:00                \_ s6-supervise
docker-host $ curl --head http://127.0.0.1/
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Mon, 17 Jan 2022 13:33:58 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Mon, 17 Jan 2022 13:32:11 GMT
Connection: keep-alive
ETag: "61e56fdb-264"
Accept-Ranges: bytes

Compatibility with v2

If you're migrating from a previous version of s6-overlay (v2) to the new version (v3), you may need to make some changes to your services or the way you use s6-overlay in order for everything to work smoothly. This document tries to be accurate on how v3 works, but we have a separate page listing the main differences, and things you're likely to notice. Please read it if you're in this situation!

Goals

The project has the following goals:

Features

The Docker Way?

One of the oft-repeated Docker mantras is "one process per container", but we disagree. There's nothing inherently bad about running multiple processes in a container. The more abstract "one thing per container" is our policy - a container should do one thing, such as "run a chat service" or "run gitlab." This may involve multiple processes, which is fine.

The other reason image authors shy away from process supervisors is they believe a process supervisor must restart failed services, meaning the Docker container will never die.

This does effectively break the Docker ecosystem - most images run one process that will exit when there's an error. By exiting on error, you allow the system administrator to handle failures however they prefer. If your image will never exit, you now need some alternative method of error recovery and failure notification.

Our policy is that if "the thing" fails, then the container should fail, too. We do this by determining which processes can restart, and which should bring down the container. For example, if cron or syslog fails, your container can most likely restart it without any ill effects, but if ejabberd fails, the container should exit so the system administrator can take action.

Our interpretation of "The Docker Way" is thus:

and our init system is designed to do exactly that. Your images will behave like other Docker images and fit in with the existing ecosystem of images.

See "Writing an optional finish script" under the Usage section for details on stopping "the thing."

Init stages

Our overlay init is a properly customized one to run appropriately in containerized environments. This section briefly explains how stages work but if you want to know how a complete init system should work, you can read this article: How to run s6-svscan as process 1

  1. stage 1: Its purpose is to set up the image to execute the supervision tree which will handle all the auxiliary services, and to launch stage 2. Stage 1 is where all the black magic happens, all the container setup details that we handle for you so that you don't have to care about them.
  2. stage 2: This is where most of the end-user provided files are meant to be executed:
    1. Execute legacy oneshot user scripts contained in /etc/cont-init.d.
    2. Run user s6-rc services declared in /etc/s6-overlay/s6-rc.d, following dependencies
    3. Copy legacy longrun user services (/etc/services.d) to a temporary directory and have s6 start (and supervise) them.
  3. stage 3: This is the shutdown stage. When the container is supposed to exit, it will:
    1. Send a TERM signal to all legacy longrun services and, if required, wait for them to exit.
    2. Bring down user s6-rc services in an orderly fashion.
    3. Run any finalization scripts contained in /etc/cont-finish.d.
    4. Send all remaining processes a TERM signal. There should not be any remaining processes anyway.
    5. Sleep for a small grace time, to allow stray processes to exit cleanly.
    6. Send all processes a KILL signal. Then the container exits.

Installation

s6-overlay comes as a set of tarballs that you can extract onto your image. The tarballs you need are a function of the image you use; most people will need the first two, and the other ones are extras you can use at your convenience.

  1. s6-overlay-noarch.tar.xz: this tarball contains the scripts implementing the overlay. We call it "noarch" because it is architecture- independent: it only contains scripts and other text files. Everyone who wants to run s6-overlay needs to extract this tarball.
  2. s6-overlay-x86_64.tar.xz: replace x86_64 with your system's architecture. This tarball contains all the necessary binaries from the s6 ecosystem, all linked statically and out of the way of your image's binaries. Unless you know for sure that your image already comes with all the packages providing the binaries used in the overlay, you need to extract this tarball.
  3. s6-overlay-symlinks-noarch.tar.xz: this tarball contains symlinks to the s6-overlay scripts so they are accessible via /usr/bin. It is normally not needed, all the scripts are accessible via the PATH environment variable, but if you have old user scripts containing shebangs such as #!/usr/bin/with-contenv, installing these symlinks will make them work.
  4. s6-overlay-symlinks-arch.tar.xz: this tarball contains symlinks to the binaries from the s6 ecosystem provided by the second tarball, to make them accessible via /usr/bin. It is normally not needed, but if you have old user scripts containing shebangs such as #!/usr/bin/execlineb, installing these symlinks will make them work.
  5. syslogd-overlay-noarch.tar.xz: this tarball contains definitions for a syslogd service. If you are running daemons that cannot log to stderr to take advantage of the s6 logging infrastructure, but hardcode the use of the old syslog() mechanism, you can extract this tarball, and your container will run a lightweight emulation of a syslogd daemon, so your syslog logs will be caught and stored to disk.

To install those tarballs, add lines to your Dockerfile that correspond to the functionality you want to install. For instance, most people would use the following:

ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz

Make sure to preserve file permissions when extracting (i.e. to use the -p option to tar.)

Usage

The project is distributed as a set of standard .tar.xz files, which you extract at the root of your image. (You need the xz-utils package for tar to understand .tar.xz files; it is available in every distribution, but not always in the default container images, so you may need to apt install xz-utils or apk add xz, or equivalent, before you can expand the archives.)

Afterwards, set your ENTRYPOINT to /init.

Right now, we recommend using Docker's ADD directive instead of running wget or curl in a RUN directive - Docker is able to handle the https URL when you use ADD, whereas your base image might not be able to use https, or might not even have wget or curl installed at all.

From there, you have a couple of options:

Using CMD

Using CMD is a convenient way to take advantage of the overlay. Your CMD can be given at build time in the Dockerfile, or at run time on the command line, either way is fine. It will be run as a normal process in the environment set up by s6; when it fails or exits, the container will shut down cleanly and exit. You can run interactive programs in this manner: only the CMD will receive your interactive command, the support processes will be unimpacted.

For example:

FROM busybox
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz
ENTRYPOINT ["/init"]
docker-host $ docker build -t s6demo .
docker-host $ docker run -ti s6demo /bin/sh
/package/admin/s6-overlay/libexec/preinit: notice: /var/run is not a symlink to /run, fixing it
s6-rc: info: service s6rc-oneshot-runner: starting
s6-rc: info: service s6rc-oneshot-runner successfully started
s6-rc: info: service fix-attrs: starting
s6-rc: info: service fix-attrs successfully started
s6-rc: info: service legacy-cont-init: starting
s6-rc: info: service legacy-cont-init successfully started
s6-rc: info: service legacy-services: starting
s6-rc: info: service legacy-services successfully started
/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 /package/admin/s6/command/s6-svscan -d4 -- /run/service
   17 root      0:00 {rc.init} /bin/sh -e /run/s6/basedir/scripts/rc.init top /bin/sh
   18 root      0:00 s6-supervise s6-linux-init-shutdownd
   20 root      0:00 /package/admin/s6-linux-init/command/s6-linux-init-shutdownd -c /run/s6/basedir -g 3000 -C -B
   24 root      0:00 s6-supervise s6rc-fdholder
   25 root      0:00 s6-supervise s6rc-oneshot-runner
   31 root      0:00 /package/admin/s6/command/s6-ipcserverd -1 -- /package/admin/s6/command/s6-ipcserver-access -v0 -E -l0 -i data/rules -- /packa
   58 root      0:00 /bin/sh
   66 root      0:00 ps
/ # exit
s6-rc: info: service legacy-services: stopping
s6-rc: info: service legacy-services successfully stopped
s6-rc: info: service legacy-cont-init: stopping
s6-rc: info: service legacy-cont-init successfully stopped
s6-rc: info: service fix-attrs: stopping
s6-rc: info: service fix-attrs successfully stopped
s6-rc: info: service s6rc-oneshot-runner: stopping
s6-rc: info: service s6rc-oneshot-runner successfully stopped
docker-host $

Writing a service script

The other way to use a container with s6-overlay is to make your services supervised. You can supervise any number of services; usually they're just support services for the main daemon you run as a CMD, but if that's what you want, nothing prevents you from having an empty CMD and running your main daemon as a supervised service as well. In that case, the daemon will be restarted by s6 whenever it exits; the container will only stop when you tell it to do so, either via a docker stop command, or from inside the container with the /run/s6/basedir/bin/halt command.

There are two ways of making a supervised service. The old way, which is still supported, is to make a "pure s6" service directory. Create a directory with the name of your service in /etc/services.d and put an executable run file into it; this is the file in which you'll put your long-lived process execution. For details of supervision of service directories, and how you can configure how s6 handles your daemon, you can take a look at the servicedir documentation. A simple example would look like this:

/etc/services.d/myapp/run:

#!/command/execlineb -P
nginx -g "daemon off;"

The new way is to make an s6-rc source definition directory in the /etc/s6-overlay/s6-rc.d directory, and add the name of that directory to the user bundle, i.e. create an empty file with the same name in the /etc/s6-overlay/s6-rc.d/user/contents.d directory. The format of a source definition directory is described in this page. Note that you can define longruns, i.e. daemons that will get supervised by s6 just like with the /etc/services.d method, but also oneshots, i.e. programs that will run once and exit. Your main service is probably a longrun, not a oneshot: you probably need a daemon to stick around.

The advantage of this new format is that it allows you to define dependencies between services: if B depends on A, then A will start first, then B will start when A is ready, and when the container is told to exit, B will stop first, then A. If you have a complex architecture where various processes depends on one another, or simply where you have to mix oneshots and longruns in a precise order, this may be for you.

The example above could be rewritten this way:

/etc/s6-overlay/s6-rc.d/myapp/type:

longrun

/etc/s6-overlay/s6-rc.d/myapp/run:

#!/command/execlineb -P
nginx -g "daemon off;"

/etc/s6-overlay/s6-rc.d/user/contents.d/myapp: empty file. (This adds myapp to the set of services that s6-rc will start at container boot.)

/etc/s6-overlay/s6-rc.d/myapp/dependencies.d/base: empty file. (This tells s6-rc to only start myapp when all the base services are ready: it prevents race conditions.)

We encourage you to switch to the new format, but if you don't need its benefits, you can stick with regular service directories in /etc/services.d, it will work just as well.

Setting the exit code of the container to the exit code of your main service

If you run your main service as a CMD, you have nothing to do: when your CMD exits, or when you run docker stop, the container will naturally exit with the same exit code as your service. (Be aware, however, that in the docker stop case, your service will get a SIGTERM, in which case the exit code will entirely depend on how your service handles it - it could trap it and exit 0, trap it and exit something else, or not trap it and let the shell exit its own code for it - normally 130.)

If you run your main service as a supervised service, however, things are different, and you need to tell the container what code to exit with when you send it a docker stop command. To do that, you need to write a finish script:

This finish script will be run when your service exits, and will take two arguments:

In the finish script, you need to write the container exit code you want to the /run/s6-linux-init-container-results/exitcode file - and that's it.

For instance, the finish script for the myapp service above could be something like this:

#!/bin/sh

if test "$1" -eq 256 ; then
  e=$((128 + $2))
else
  e="$1"
fi

echo "$e" > /run/s6-linux-init-container-results/exitcode

When you send a docker stop command to your container, the myapp service will be killed and this script will be run; it will write either myapp's exit code (if myapp catches the TERM signal) or 130 (if myapp does not catch the TERM signal) to the special /run/s6-linux-init-container-results/exitcode file, which will be read by s6-overlay at the end of the container shutdown procedure, and your container will exit with that value.

Fixing ownership and permissions

This section describes a functionality from the versions of s6-overlay that are anterior to v3. fix-attrs is still supported in v3, but is deprecated, for several reasons: one of them is that it's generally not good policy to change ownership dynamically when it can be done statically. Another reason is that it doesn't work with USER containers. Instead of fix-attrs, we now recommend you to take care of ownership and permissions on host mounts offline, before running the container. This should be done in your Dockerfile, when you have all the needed information.

That said, here is what we wrote for previous versions and that is still applicable today (but please stop depending on it):

Sometimes it's interesting to fix ownership & permissions before proceeding because, for example, you have mounted/mapped a host folder inside your container. Our overlay provides a way to tackle this issue using files in /etc/fix-attrs.d. This is the pattern format followed by fix-attrs files:

path recurse account fmode dmode

Here you have some working examples:

/etc/fix-attrs.d/01-mysql-data-dir:

/var/lib/mysql true mysql 0600 0700

/etc/fix-attrs.d/02-mysql-log-dirs:

/var/log/mysql-error-logs true nobody,32768:32768 0644 2700
/var/log/mysql-general-logs true nobody,32768:32768 0644 2700
/var/log/mysql-slow-query-logs true nobody,32768:32768 0644 2700

Executing initialization and finalization tasks

Here is the old way of doing it:

After fixing attributes (through /etc/fix-attrs.d/) and before starting user provided services (through s6-rc or /etc/services.d) our overlay will execute all the scripts found in /etc/cont-init.d, for example:

/etc/cont-init.d/02-confd-onetime:

#!/command/execlineb -P

with-contenv
s6-envuidgid nginx
multisubstitute
{
  import -u -D0 UID
  import -u -D0 GID
  import -u CONFD_PREFIX
  define CONFD_CHECK_CMD "/usr/sbin/nginx -t -c {{ .src }}"
}
confd --onetime --prefix="${CONFD_PREFIX}" --tmpl-uid="${UID}" --tmpl-gid="${GID}" --tmpl-src="/etc/nginx/nginx.conf.tmpl" --tmpl-dest="/etc/nginx/nginx.conf" --tmpl-check-cmd="${CONFD_CHECK_CMD}" etcd

This way is still supported. However, there is now a more generic and efficient way to do it: writing your oneshot initialization and finalization tasks as s6-rc services, by adding service definition directories in /etc/s6-overlay/s6-rc.d, making them part of the user bundle (so they are actually started when the container boots), and making them depend on the base bundle (so they are only started after base).

All the information on s6-rc can be found here.

When the container is started, the operations are performed in this order:

When the container is stopped, either because the admin sent a stop command or because the CMD exited, the operations are performed in the reverse order:

The point of the user2 bundle is to allow user services declared in it to start after the /etc/services.d ones; but in order to do so, every service in user2 needs to declare a dependency to legacy-services. In other words, for a service foobar to start late, you need to:

That will ensure that foobar will start after everything in /etc/services.d.

Writing an optional finish script

By default, services created in /etc/services.d will automatically restart. If a service should bring the container down, you should probably run it as a CMD instead; but if you'd rather run it as a supervised service, then you'll need to write a finish script, which will be run when the service is down; to make the container stop, the /run/s6/basedir/bin/halt command must be invoked. Here's an example finish script:

/etc/services.d/myapp/finish:

#!/command/execlineb -S0

foreground { redirfd -w 1 /run/s6-linux-init-container-results/exitcode echo 0 }
/run/s6/basedir/bin/halt

The first line of the script writes 0 to the /run/s6-linux-init-container-results/exitcode file. The second line stops the container. When you stop the container via the /run/s6/basedir/bin/halt command run from inside the container, /run/s6-linux-init-container-results/exitcode is read and its contents are used as the exit code for the docker run command that launched the container. If the file doesn't exist, or if the container is stopped with docker stop or another reason, that exit code defaults to 0.

It is possible to do more advanced operations in a finish script. For example, here's a script from that only brings down the service when it exits nonzero:

/etc/services.d/myapp/finish:

#!/command/execlineb -S1
if { eltest ${1} -ne 0 -a ${1} -ne 256 }
/run/s6/basedir/bin/halt

Note that in general, finish scripts should only be used for local cleanups after a daemon dies. If a service is so important that the container needs to stop when it dies, we really recommend running it as the CMD.

Logging

Every service can have its dedicated logger. A logger is a s6 service that automatically reads from the stdout of your service, and logs the data to an automatically rotated file in the place you want. Note that daemons usually log to stderr, not stdout, so you should probably start your service's run script with exec 2>&1 in shell, or with fdmove -c 2 1 in execline, in order to catch stderr.

s6-overlay provides a utility called logutil-service which is a wrapper over the s6-log program. This helper does the following:

s6-log will then run forever, reading data from your service and writing it to the directory you specified to logutil-service.

Please note:

You can create log folders in cont-init.d scripts, or as s6-rc oneshots. Here is an example of a logged service myapp implemented the old way:

/etc/cont-init.d/myapp-log-prepare:

#!/bin/sh -e
mkdir -p /var/log/myapp
chown nobody:nogroup /var/log/myapp
chmod 02755 /var/log/myapp

/etc/services.d/myapp/run:

#!/bin/sh
exec 2>&1
exec mydaemon-in-the-foreground-and-logging-to-stderr

/etc/services.d/myapp/log/run:

#!/bin/sh
exec logutil-service /var/log/myapp

And here is the same service, myapp, implemented in s6-rc.

/etc/s6-overlay/s6-rc.d/myapp-log-prepare/dependencies.d/base: empty file

/etc/s6-overlay/s6-rc.d/myapp-log-prepare/type:

oneshot

/etc/s6-overlay/s6-rc.d/myapp-log-prepare/up:

if { mkdir -p /var/log/myapp }
if { chown nobody:nogroup /var/log/myapp }
chmod 02755 /var/log/myapp
<details><summary>(Click here for an explanation of the weird syntax or if you don't understand why your `up` file isn't working.)</summary> <p>

(Beginning of the detailed section.)

So, the up and down files are special: they're not shell scripts, but single command lines interpreted by execlineb. You should not have to worry about execline; you should only remember that an up file contains a single command line. So if you need a script with several instructions, here's how to do it:

Here is how you would normally proceed to write the up file for myapp-log-prepare:

/etc/s6-overlay/s6-rc.d/myapp-log-prepare/up:

/etc/s6-overlay/scripts/myapp-log-prepare

/etc/s6-overlay/scripts/myapp-log-prepare: (needs to be executable)

#!/bin/sh -e
mkdir -p /var/log/myapp
chown nobody:nogroup /var/log/myapp
chmod 02755 /var/log/myapp

The location of the actual script is arbitrary, it just needs to match what you're writing in the up file.

But here, it just so happens that the script is simple enough that it can fit entirely in the up file without making it too complex or too difficult to understand. So, we chose to include it as an example to show that there's more that you can do with up files, if you are so inclined. You can read the full documentation for the execline language here.

(End of the detailed section, click the triangle above again to collapse.)

</p> </details>

/etc/s6-overlay/s6-rc.d/myapp/dependencies.d/base: empty file

/etc/s6-overlay/s6-rc.d/myapp-log/dependencies.d/myapp-log-prepare: empty file

/etc/s6-overlay/s6-rc.d/myapp/type:

longrun

/etc/s6-overlay/s6-rc.d/myapp/run:

#!/bin/sh
exec 2>&1
exec mydaemon-in-the-foreground-and-logging-to-stderr

/etc/s6-overlay/s6-rc.d/myapp/producer-for:

myapp-log

/etc/s6-overlay/s6-rc.d/myapp-log/type:

longrun

/etc/s6-overlay/s6-rc.d/myapp-log/run:

#!/bin/sh
exec logutil-service /var/log/myapp

/etc/s6-overlay/s6-rc.d/myapp-log/consumer-for:

myapp

/etc/s6-overlay/s6-rc.d/myapp-log/pipeline-name:

myapp-pipeline

/etc/s6-overlay/s6-rc.d/user/contents.d/myapp-pipeline: empty file

That's a lot of files! A summary of what it all means is:

It really accomplishes the same things as the /etc/cont-init.d plus /etc/services.d method, but it's a lot cleaner underneath, and can handle much more complex dependency graphs, so whenever you get the opportunity, we recommend you familiarize yourself with the s6-rc way of declaring your services and your loggers. The full syntax of a service definition directory, including declaring whether your service is a longrun or a oneshot, declaring pipelines, adding service-specific timeouts if you need them, etc., can be found here.

Dropping privileges

When it comes to executing a service, no matter whether it's a service or a logger, a good practice is to drop privileges before executing it. s6 already includes utilities to do exactly these kind of things:

In execline:

#!/command/execlineb -P
s6-setuidgid daemon
myservice

In sh:

#!/bin/sh
exec s6-setuidgid daemon myservice

If you want to know more about these utilities, please take a look at: s6-setuidgid, s6-envuidgid, and s6-applyuidgid.

Container environment

If you want your custom script to have container environments available: you can use the with-contenv helper, which will push all of those into your execution environment, for example:

/etc/cont-init.d/01-contenv-example:

#!/command/with-contenv sh
env

This script will output the contents of your container environment.

Read-Only Root Filesystem

Recent versions of Docker allow running containers with a read-only root filesystem. If your container is in such a case, you should set S6_READ_ONLY_ROOT=1 to inform s6-overlay that it should not attempt to write to certain areas - instead, it will perform copies into a tmpfs mounted on /run.

Note that s6-overlay assumes that:

In general your default docker settings should already provide a suitable tmpfs in /run.

Customizing s6-overlay behaviour

It is possible somehow to tweak s6-overlay's behaviour by providing an already predefined set of environment variables to the execution context:

syslog

If software running in your container requires syslog, extract the syslogd-overlay-noarch.tar.xz tarball: that will give you a small syslogd emulation. Logs will be found under various subdirectories of /var/log/syslogd, for instance messages will be found in the /var/log/syslogd/messages/ directory, the latest logs being available in the /var/log/syslogd/messages/current file. Logging directories are used rather than files so that logs can be automatically rotated without race conditions (that is a feature of s6-log).

It is recommended to add syslog and sysllog users to your image, for privilege separation; the syslogd emulation processes will run as these users if they exist. Otherwise they will default to 32760:32760 and 32761:32761, numeric uids/gids that may already exist on your system.

Performance

Verifying Downloads

The s6-overlay releases have a checksum files you can use to verify the download using SHA256:

ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz.sha256 /tmp
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz.sha256 /tmp
RUN cd /tmp && sha256sum -c *.sha256

Notes

USER directive

As of version 3.2.0.2, s6-overlay has limited support for running as a user other than root:

Generally speaking, if you're running a simple container with a main application and one or two support services, you may benefit from the USER directive if that is your preferred way of running containers. However, if you're running more than a few services, or daemons that expect a real system with complete Unix infrastructure, then USER is probably not a good idea and you would benefit more from using privilege separation between services in your container.

Terminal support

Generally speaking, you should not run your containers with docker run -it. It is bad practice to have console access to your containers. That said, if your CMD is interactive and needs a terminal, s6-overlay will try to support it whenever possible, but the nature of terminals makes it difficult to ensure that everything works perfectly in all cases.

In particular, if you are stacking virtualization environments and other layers already have their own kludges for terminals - for instance, if you are running s6-overlay under qemu - then it is almost guaranteed that docker run -it will not work. However, once the container is running, you should always be able to access an interactive shell inside it via docker exec -it containername /bin/sh.

The same caveats apply to stopping containers with ^C. Normally containers are stopped via docker stop, or when the CMD exits; ^C is not an officially supported method of stopping them. s6-overlay tries to exit cleanly on ^C, whether the container is running with -it or not, but there will be cases where it is unfortunately impossible.

Releases

Over on the releases tab, we have a number of tarballs:

We have binaries for at least x86_64, aarch64, arm32, i486, i686, riscv64, and s390x. The full list of supported arches can be found in conf/toolchains.

Which architecture to use depending on your TARGETARCH

The ${arch} part in the s6-overlay-${arch}.tar.xz tarball uses the naming conventions of gcc, which are not the ones that Docker uses. (Everyone does something different in this field depending on their needs, and no solution is better than any other, but the Docker one is worse than others because its naming is inconsistent. The gcc convention is better for us because it simplifies our builds greatly and makes them more maintainable.)

The following table should help you find the right tarball for you if you're using the TARGETARCH value provided by Docker:

${TARGETARCH}${arch}Notes
amd64x86_64
arm64aarch64
arm/v7armarmv7 with soft-float
arm/v6armhfRaspberry Pi 1
386i686i486 for very old hw
riscv64riscv64
s390xs390x

If you need another architecture, ask us and we'll try to make a toolchain for it. In particular, we know that armv7 is a mess and needs a flurry of options depending on your precise target (and this is one of the reasons why the Docker naming system isn't good, although arguably the gcc naming system isn't much better on that aspect).

Contributing

Any way you want! Open issues, open PRs, we welcome all contributors!

Building the overlay yourself

Upgrade Notes

Please see CHANGELOG.