Normally, if a program doesn't set a SIGTERM handler, it needs --init:
Dockerfile:
FROM alpine:3.20
COPY a.c .
RUN apk add build-base \
&& gcc a.ca.c:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <limits.h>
#include <string.h>
void handler(int sig) {
printf("shutting down...\n");
exit(0);
}
int main(int argc, char *argv[]) {
if (argc > 1 && strcmp(argv[1], "--set-signal-handler") == 0)
signal(SIGTERM, handler);
for (;;) sleep(INT_MAX);
return EXIT_SUCCESS;
}$ docker build -t i .
$ docker run --rm i ./a.out
$ docker exec `docker ps -q | head -1` pkill a.out
// it doesn't stop
$ docker kill -s KILL `docker ps -q | head -1`
$ docker run --rm i ./a.out --set-signal-handler
$ docker exec `docker ps -q | head -1` pkill a.out
shutting down...
$ docker run --rm --init i ./a.out
$ docker exec `docker ps -q | head -1` pkill a.out
$ docker run --rm --init i ./a.out --set-signal-handler
$ docker exec `docker ps -q | head -1` pkill a.out
shutting down...
python:
$ docker run --rm python:3.13.0-alpine3.20 python -c 'import time; time.sleep(1000)'
$ docker exec `docker ps -q | head -1` pkill python
// it doesn't stop
$ docker kill -s KILL `docker ps -q | head -1`
$ docker run --rm python:3.13.0-alpine3.20 \
python -c 'import signal; import time; import sys; signal.signal(signal.SIGTERM, lambda signum, frame: (print("shutting down..."), sys.exit(0))); time.sleep(1000)'
$ docker exec `docker ps -q | head -1` pkill python
shutting down...
$ docker run --rm --init python:3.13.0-alpine3.20 python -c 'import time; time.sleep(1000)'
$ docker exec `docker ps -q | head -1` pkill python
$ docker run --rm --init python:3.13.0-alpine3.20 \
python -c 'import signal; import time; import sys; signal.signal(signal.SIGTERM, lambda signum, frame: (print("shutting down..."), sys.exit(0))); time.sleep(1000)'
$ docker exec `docker ps -q | head -1` pkill python
shutting down...
perl:
$ docker run --rm perl:5.41.4-slim perl -e 'sleep'
$ docker kill -s TERM `docker ps -q | head -1`
// it doesn't stop
$ docker kill -s KILL `docker ps -q | head -1`
$ docker run --rm perl:5.41.4-slim \
perl -e '$SIG{TERM} = sub { print "shutting down..."; exit 0 }; sleep'
$ docker kill -s TERM `docker ps -q | head -1`
shutting down...
$ docker run --rm --init perl:5.41.4-slim perl -e 'sleep'
$ docker kill -s TERM `docker ps -q | head -1`
$ docker run --rm --init perl:5.41.4-slim \
perl -e '$SIG{TERM} = sub { print "shutting down..."; exit 0 }; sleep'
$ docker kill -s TERM `docker ps -q | head -1`
shutting down...
But there are languages that set their own handlers before execution of user code starts.
ruby:
$ docker run --rm ruby:3.3.5-alpine3.20 ruby -e 'sleep'
$ docker exec `docker ps -q | head -1` pkill ruby
$ docker run --rm ruby:3.3.5-alpine3.20 ruby -e 'Signal.trap("TERM") do puts "shutting down..."; exit end; sleep'
$ docker exec `docker ps -q | head -1` pkill ruby
shutting down...
$ docker run --rm --init ruby:3.3.5-alpine3.20 ruby -e 'sleep'
$ docker exec `docker ps -q | head -1` pkill ruby
$ docker run --rm --init ruby:3.3.5-alpine3.20 ruby -e 'Signal.trap("TERM") do puts "shutting down..."; exit end; sleep'
$ docker exec `docker ps -q | head -1` pkill ruby
shutting down...
go:
Dockerfile:
FROM alpine:3.20
COPY a.go .
RUN apk add go && GO111MODULE=off go builda.go:
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
if len(os.Args) > 1 && os.Args[1] == "--set-signal-handler" {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGTERM)
go func() {
<-sigChan
fmt.Printf("shutting down...\n")
os.Exit(0)
}()
}
for {
time.Sleep(1 * time.Second)
}
}$ docker build -t i -f Dockerfile2 .
$ docker run --rm i ./_
$ docker exec `docker ps -q | head -1` pkill _
$ docker run --rm i ./_ --set-signal-handler
$ docker exec `docker ps -q | head -1` pkill _
shutting down...
$ docker run --rm --init i ./_
$ docker exec `docker ps -q | head -1` pkill _
$ docker run --rm --init i ./_ --set-signal-handler
$ docker exec `docker ps -q | head -1` pkill _
shutting down...
So with ruby and go programs you probably won't have problems. If a program doesn't set a SIGTERM handler, then the cleanup is apparently not needed, the language runtime will set a handler and it'll terminate on SIGTERM. Otherwise the program's SIGTERM handler will terminate it.
In languages where it's not the case to determine if you need --init run the program in a container, make sure it's running under PID 1 and send SIGTERM to it. If it terminates, then --init apparently is not needed.