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.c
a.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 build
a.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.