Docker CMD and ENTRYPOINT Tester

Work out what a container actually executes, and what ends up as PID 1. The answer decides whether docker stop is graceful or a ten second wait followed by a kill.

Paste below, or drop a file anywhere on this panel

Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.

The answer appears here

Paste on the left and press Test. Nothing leaves this tab.

Examples

Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.

Shell form

PID 1 is a shell, so docker stop waits the full grace period and then kills

CMD /app/server --port 8080

A shell ENTRYPOINT with a CMD

The CMD is thrown away entirely, and nothing says so

ENTRYPOINT /app/server
CMD ["--port", "8080"]

Run arguments over a CMD

They replace the whole CMD rather than merging with it

ENTRYPOINT ["/app/server"]
CMD ["--port", "8080"]
docker run -d myimage --verbose

Common mistakes

These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

  1. Using shell form for a long-running service

    PID 1 becomes /bin/sh, which does not forward SIGTERM. Every stop waits the full grace period and ends in SIGKILL, so no shutdown handler ever runs.

    Instead:Use exec form: ENTRYPOINT ["/app/server"]. If a shell is genuinely needed, exec through it so your process replaces the shell.

  2. Pairing a shell-form ENTRYPOINT with a CMD

    CMD is only appended to an exec-form ENTRYPOINT. In shell form it is discarded, so the defaults you wrote are never used and nobody is told.

    Instead:Put the ENTRYPOINT in exec form, then CMD becomes its default arguments as intended.

  3. Putting flags that must always apply in the CMD

    Any argument on docker run replaces the whole CMD, so all the defaults disappear at once.

    Instead:Keep required flags in the ENTRYPOINT and use CMD only for things a caller should be able to change.

Shell form puts a shell at PID 1, and a shell does not forward SIGTERM

That single fact is behind most of the questions about why containers take so long to stop and why shutdown handlers never run.

What actually happens on docker stop

Docker sends SIGTERM to PID 1 and waits ten seconds. With shell form, PID 1 is /bin/sh -c and your program is its child, so the shell receives the signal, does nothing with it, and your process never hears about it. After the grace period Docker sends SIGKILL. Every stop takes the full ten seconds, no cleanup runs, and in-flight requests are dropped rather than drained.

Exec form runs the program directly

ENTRYPOINT ["/app/server"] makes your binary PID 1, so the signal arrives where the handler is. This is the form to use for anything long-running, and the only reason not to is if you genuinely need shell features like variable expansion or a pipe.

If you must use a shell, exec through it

ENTRYPOINT ["/bin/sh", "-c", "exec /app/server"] runs the shell, and exec replaces the shell process with yours, so your program ends up as PID 1 anyway. Without the exec keyword the shell stays and the problem returns.

A shell-form ENTRYPOINT throws the CMD away

CMD is appended to ENTRYPOINT only when ENTRYPOINT is in exec form. In shell form the whole thing becomes one string and the CMD is never used, so default arguments silently do nothing and arguments passed to docker run do nothing either.

Arguments on the run line replace the CMD entirely

They do not merge. A CMD carrying three default flags loses all three the moment somebody passes one argument, which is how a container ends up running without flags nobody realised were defaults. Anything that must always apply belongs in the ENTRYPOINT.

PID 1 has one more job

It reaps orphaned child processes. A program that never wrote that code accumulates zombies when it spawns subprocesses. --init inserts a tiny init that handles both reaping and signal forwarding, and is the right answer when the program at PID 1 was never designed to be one.