# 7. PID 1 and signals [← 6. AOT cache](06-aot-cache.md) · [Index](../README.md) · Next: [8. Living with distroless →](08-distroless-in-practice.md) `docker stop` - and Kubernetes, on pod deletion - sends SIGTERM to PID 1, waits (10 s for Docker, `terminationGracePeriodSeconds` for Kubernetes), then SIGKILLs. Spring Boot's graceful shutdown only runs if the JVM receives that SIGTERM. [`pid1-and-signals.txt`](output/pid1-and-signals.txt): ``` layered-jre PID 1: java -jar application.jar docker stop: 244 ms exit code: 143 graceful-shutdown log lines: 1 shell-form PID 1: /bin/sh -c java -jar /app/app.jar docker stop: 10160 ms exit code: 137 graceful-shutdown log lines: 0 shell-form-alpine PID 1: java -jar /app/app.jar docker stop: 219 ms exit code: 143 graceful-shutdown log lines: 1 shell-form-wrapper PID 1: /bin/sh -c echo "starting revision $(date +%s)" && java -j docker stop: 10176 ms exit code: 137 graceful-shutdown log lines: 0 ``` - **Exec form** (`["java", "-jar", ...]`): the JVM is PID 1, SIGTERM arrives, graceful shutdown runs, exit code 143, a quarter of a second. - **Shell form on `eclipse-temurin:25-jre`**: `/bin/sh` is dash (`/usr/bin/dash`). dash stays resident as PID 1 and does not forward SIGTERM. Docker waits the full 10 s and SIGKILLs: exit 137, no graceful shutdown, in-flight requests cut off. - **The same shell form on the Alpine image works**, because `/bin/sh` there is BusyBox ash, which replaces itself with the last command of a `-c` string. The same Dockerfile line behaves differently depending on the base image - which is how "it works on my image" arguments start. - **Any shell form with more than one command** keeps the shell as PID 1 regardless. If you need a wrapper script, end it with `exec java ...`. Distroless removes the question: with no `/bin/sh`, a shell-form `ENTRYPOINT` cannot even start.