Java in a containerhard5-8 years

A service's Dockerfile ends with `CMD java -jar app.jar` (shell form, no brackets). `docker stop` on the running container takes the full 10-second grace period every time, and the JVM never runs its shutdown hooks. What's actually happening, and what fixes it?

Shell-form CMD java -jar app.jar (no square brackets) is run as /bin/sh -c "java -jar app.jar", which means the shell, not the JVM, becomes PID 1 inside the container — and PID 1 is exactly the process docker stop sends SIGTERM to. The shell forks Java as a separate child process to actually execute the command, so the SIGTERM reaches the shell, not the JVM, and a shell doesn't automatically forward a signal it receives to a child it happened to fork. The JVM never gets a SIGTERM at all, so Spring Boot's graceful shutdown and any registered shutdown hooks never run — Docker waits out the full grace period with nothing happening, then sends SIGKILL, which ends the JVM mid-request with none of its shutdown logic having executed. Exec-form ENTRYPOINT/CMD (["java", "-jar", "app.jar"], a JSON array, no shell) fixes it because it runs Java directly as PID 1, with no shell process forked in between — the signal Docker sends to PID 1 lands on the JVM itself.

The lesson behind it →
More on Java in a container