Signals and graceful shutdown

Docker · lesson 17 of 31 · 4 min read

Make docker stop end your JVM cleanly instead of the daemon killing it ten seconds later.

Open this lesson in the learning hub

Key points

  • docker stop sends SIGTERM to PID 1, waits 10 s, then SIGKILL. docker kill skips the wait.
  • Spring Boot installs a shutdown hook for SIGTERM. Add server.shutdown=graceful and it drains in-flight requests first.
  • An exec-form ENTRYPOINT is what makes the JVM PID 1, and therefore the process that receives the signal at all.
  • Exit 143 is 128+15: a clean SIGTERM stop. Exit 137 is 128+9: killed, either by the stop timeout or by the OOM killer.
  • Shutdown genuinely slow? Widen the window with docker stop -t 30 and match it in the app timeout property.
  • If your entrypoint is a script that spawns children, add --init so a real init reaps the zombies.

Example

# Dockerfile - exec form, so java is PID 1 and hears SIGTERM
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
# STOPSIGNAL SIGTERM is already the default; set it only if you need another

# application.yaml
#   server.shutdown: graceful
#   spring.lifecycle.timeout-per-shutdown-phase: 25s

# give it 30 seconds instead of the default 10
docker stop -t 30 api

# 143 = clean SIGTERM stop.  137 = it was killed.
docker inspect --format "{{.State.ExitCode}} {{.State.OOMKilled}}" api

Exit 143 means you shut down. Exit 137 means something shut you down.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Docker course, and every lesson in it is listed on the Docker contents page.