How to Make Supervisor Stop All Processes When One Exits Successfully (Exit Code 0) in Docker


7 views

When running multiple processes under Supervisor in Docker containers, we often want the entire container to terminate when the main application process completes successfully. However, other background processes (like serf-agent in this case) may keep the container running even after the primary process exits.

Supervisor is designed to manage multiple processes, and by default, it will keep running as long as there are processes it needs to supervise. The key configuration options we need to focus on are:

stopasgroup=true
killasgroup=true
autorestart=unexpected

Here's how to modify your configuration to achieve the desired behavior:

[program:producer]
command=/start.sh
numprocs=1
stopasgroup=true
killasgroup=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
exitcodes=0          # Important: Only treat exit code 0 as success
stopsignal=TERM      # Signal to use for stopping
stopwaitsecs=10      # Wait time before force killing

For the serf agent, modify its configuration:

[program:serf]
command=/start-serf-agent.sh
numprocs=1
autostart=true
autorestart=false    # Don't restart if it exits
stopasgroup=true
killasgroup=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

Update your run.sh script to monitor the producer process:

#!/usr/bin/env bash

# Start supervisor in the background
supervisord -n &

# Wait for producer to exit
while sleep 1; do
    if supervisorctl status producer | grep -q 'EXITED'; then
        # Check exit status
        if supervisorctl status producer | grep -q 'EXITED 0'; then
            # If producer exited successfully, stop everything
            supervisorctl shutdown
            exit 0
        fi
    fi
done

For a more elegant solution, you can implement a Supervisor event listener:

[eventlistener:process_exit]
command=/exit_listener.sh
events=PROCESS_STATE_EXITED
autostart=true

Then create exit_listener.sh:

#!/bin/bash

while read line; do
    # Check if producer exited with code 0
    if [[ "$line" == *"producer"*"EXITED 0"* ]]; then
        supervisorctl shutdown
        exit 0
    fi
done < "${SUPERVISOR_PROCESS_STATE_STDIN}"

Make sure your Docker container properly handles signals by using exec in your CMD:

CMD ["exec", "/run.sh"]

This ensures signals are properly propagated to the supervisor process.


When running multiple processes under Supervisor in a Docker container, you might encounter situations where one process exits successfully (status 0), but others keep running, preventing the container from stopping. This is particularly common when you have:

- A main application process
- Supporting processes (like Serf agent in this case)
- Processes managed by Supervisor

The existing configuration shows Supervisor managing two processes:

[group:job]
programs=serf,producer

While stopasgroup and killasgroup are set to true, this only affects what happens when Supervisor tries to stop the processes - not when a process exits naturally.

1. Supervisor Event Listener

Create an event listener that watches for PROCESS_STATE_EXITED events and terminates Supervisor when the producer exits with status 0:

[eventlistener:shutdown]
command=/shutdown-listener.sh
events=PROCESS_STATE_EXITED
autorestart=false

shutdown-listener.sh:

#!/bin/bash
while read line; do
  if [[ "$line" == *"producer"* && "$line" == *"exited status 0"* ]]; then
    kill -s SIGTERM $(cat /var/run/supervisord.pid)
  fi
done < "${SUPERVISOR_HTTP_SERVER_FILE:-/dev/stdin}"

2. Process Dependency Configuration

Modify your configuration to make serf dependent on producer:

[program:serf]
command=/start-serf-agent.sh
autorestart=false
exitcodes=0

3. Docker-aware Solution

Modify your run.sh to monitor the producer process:

#!/usr/bin/env bash
supervisord -n &
SUPER_PID=$!

# Wait for producer to exit
while sleep 1; do
  if ! supervisorctl status producer | grep -q RUNNING; then
    if supervisorctl status producer | grep -q "EXITED 0"; then
      kill $SUPER_PID
      exit 0
    fi
  fi
done

The most robust solution combines approaches:

  1. Use the event listener pattern for clean shutdown
  2. Set proper exitcodes and autorestart policies
  3. Consider process dependencies in your architecture

For production environments, I recommend the event listener approach as it's the most supervisor-native solution and provides the cleanest shutdown sequence.