Operations 11 min read

How to Gracefully Shut Down Docker Containers Using Signals and Exec Format

This article explains Linux signals, their role in Docker container shutdown, and demonstrates with Go and shell examples how using the exec form in Dockerfile ENTRYPOINT/CMD ensures containers receive termination signals for a clean exit.

MaGe Linux Operations
MaGe Linux Operations
MaGe Linux Operations
How to Gracefully Shut Down Docker Containers Using Signals and Exec Format

Recently the Docker Reference documentation was revisited, focusing on Linux signals and how they enable graceful container shutdown via Dockerfile ENTRYPOINT and CMD.

1 Signal

Signal is a notification mechanism for processes, sometimes called a software interrupt.

Linux defines standard signals numbered 1–31, which can be listed with kill -l:

# kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT
 4) SIGILL       5) SIGTRAP      6) SIGABRT
 7) SIGBUS       8) SIGFPE       9) SIGKILL
10) SIGUSR1    11) SIGSEGV     12) SIGUSR2
13) SIGPIPE    14) SIGALRM     15) SIGTERM
... ...

Commonly used signals include: SIGHUP – sent when a terminal disconnects; daemons often reload configuration on receipt. SIGINT – generated by Ctrl‑C ; default action terminates the process. SIGQUIT – generated by Ctrl‑\ ; terminates the process and creates a core dump. SIGKILL – cannot be caught or ignored; forces immediate termination. SIGTERM – standard termination signal used by kill, killall, pkill; applications should handle it to clean up resources before exiting. SIGTSTP – job‑control stop signal generated by Ctrl‑Z ; pauses the foreground process group.

Note that Ctrl‑D does not send a signal; it signals EOF and closes standard input.

Programs can install handlers to catch signals and execute custom logic.

Most of this knowledge comes from the "Linux/UNIX System Programming Manual" (chapters 20‑22).

2 ENTRYPOINT, CMD

The relationship between signals and graceful shutdown is tied to how Dockerfile defines the container’s start command. CMD supports three formats: CMD ["executable","param1","param2"] – exec form (recommended). CMD ["param1","param2"] – parameters for ENTRYPOINT. CMD command param1 param2 – shell form (runs via /bin/sh -c). ENTRYPOINT has two formats: ENTRYPOINT ["executable","param1","param2"] – exec form (recommended). ENTRYPOINT command param1 param2 – shell form.

When the shell form is used, the process runs under /bin/sh -c, which does not forward signals to the actual program. Consequently, docker stop cannot deliver the termination signal, and the container is killed forcefully.

Using the exec form ensures the program receives signals directly.

docker stop [OPTIONS] CONTAINER
  -t, --time int   Seconds to wait before killing (default 10)
docker kill [OPTIONS] CONTAINER
  -s, --signal string   Signal to send (default "KILL")
docker stop

first sends SIGTERM; if the container does not exit within the timeout, SIGKILL is sent.

Conclusion: prefer the exec format in Dockerfile for graceful shutdown.

3 Examples

3.1 Example 1

Go program that handles SIGINT and SIGTERM:

package main
import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
)
func main() {
    sigs := make(chan os.Signal, 1)
    done := make(chan bool, 1)
    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
    go func() {
        sig := <-sigs
        fmt.Println()
        fmt.Println(sig)
        done <- true
    }()
    fmt.Println("awaiting signal")
    <-done
    fmt.Println("exiting")
}

Dockerfile using exec CMD:

FROM busybox
COPY signals /signals
CMD ["/signals"]

Running the container and stopping it with docker stop shows the program receives the signal and exits in ~0.73 s.

Changing the Dockerfile to use the shell form:

FROM busybox
COPY signals /signals
CMD /signals

Now docker stop forces termination after the default 10 s timeout because the signal is not propagated.

3.2 Example 2

When CMD exec runs a shell script, signals are still not passed. Dockerfile:

FROM busybox
COPY signals /signals
COPY start.sh /start.sh
CMD ["/start.sh"]

Shell script:

#!/bin/sh
/signals

Even with exec CMD, the container is force‑stopped because the script runs under /bin/sh. Adding exec inside the script forwards signals:

#!/bin/sh
exec /signals

Rebuilding and running the container now allows graceful shutdown in ~0.74 s.

Therefore, to achieve graceful container termination, use the exec form for both Dockerfile instructions and any wrapper scripts, and ensure the application handles termination signals.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DockerContainerDockerfileGraceful Shutdownsignals
MaGe Linux Operations
Written by

MaGe Linux Operations

Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.