How-To Use supervisord in Docker Images

Supervisord is “a client/server system that allows its users to monitor and control a number of processes on UNIX-like operating systems.”

  • To install in your Dockefile get it from the epel repository – you have to enable epel first, of course.
    FROM rhel7
    ...
    yum install -y --enablerepo=epel supervisor
  • Config is in /etc/supervisord.conf
    You have to set “nodaemon=true”, so that supervisord will start in foreground

    [supervisord]
    nodaemon=true
    ...
  • Important is, that unix signals are passed to supervisord, so that there’ll be no zombies in case of deletion of the container
    • Find this blog post about signals in docker containers
    • Supervisord will handle it’s subprocesses according to the signals it gets. So it is important, that it runs as PID 1 in the container and is not started by a shell.
      • So verify, that supervisord is started in the exec format
    • If you manage a shell script with supervisord, ensure, that you catch relevant signals within you script and proceed accordingly
      ...
      function clean_up {
              # Perform program cleanup
              ...
              exit 0
      }
      trap clean_up SIGHUP SIGINT SIGTERM
  • If you want to manage a daemon process with your supervisord, you have to ensure, that it runs in the foreground – most daemon start commands are supporting this. Otherwise, you could use this script. See this post.
    #! /usr/bin/env bash
    set -eu
    pidfile="/var/run/your-daemon.pid"
    command=/usr/sbin/your-daemon
    # Proxy signals
    function kill_app(){
        kill $(cat $pidfile)
        exit 0 # exit okay
    }
    trap "kill_app" SIGINT SIGTERM
    # Launch daemon
    exec $command
    sleep 2
    # Loop while the pidfile and the process exist
    while [ -f $pidfile ] && kill -0 $(cat $pidfile) ; do
        sleep 0.5
    done
    exit 1000 # exit unexpected

    “kill -0” doesn’t send any kill signals, it only checks, if the permissions are sufficient to kill the process.

  • Don’t use “sleep inf” or “tail -f /dev/null” at the end of your scripts in order to block its ending. They won’t pass unix signals. Instead as described above, use a loop with a short sleep
  • Also important to mention is that the default behaviour of supervisord is to NOT restart programs when they finish with an exit code of “0”.
    • So ensure, that in case of crashes, your programs, daemons etc. are exiting with a non-0 state, so that supervisord knows that it should have to restart them
  • Supervisord will not end, when all programs have been finished. So even if all your programs have been exited with code 0, the supervisor process will run further on. If you want to have another behaviour, you have to implement an  event listener.
  • If supervisord is killed, it waits for the programs to be ended before it terminates
  • Here’s a test script I used for the investigations
    #!/bin/bash
    set -e
    echo "This program is running as PID $$ "
    function trap_with_arg() {
        func="$1" ; shift
        for sig ; do
            trap "$func $sig" "$sig"
        done
    }
    function clean_up {
        # Perform program exit
        echo Trapped: $1
        if [[ "$1" == "SIGTERM" ]] ; then
            exit 0
        else
            exit 1
        fi
    }
    trap_with_arg clean_up SIGHUP SIGINT SIGTERM
    while /bin/true ; do
        sleep 0.5
    done