juniorDocker

What is the difference between CMD and ENTRYPOINT in a Dockerfile?

Updated Feb 20, 2026

Short answer

ENTRYPOINT sets the executable the container always runs; CMD supplies default arguments that a user can override at docker run. Used together in exec form, ENTRYPOINT makes the image behave like a command and CMD gives it sensible defaults. Used alone, CMD is entirely replaced by anything the user passes.

Deep explanation

DOCKERFILE
ENTRYPOINT ["ping"]
CMD ["-c", "3", "localhost"]
  • docker run imgping -c 3 localhost
  • docker run img google.comping google.com (CMD replaced, ENTRYPOINT kept)

With only CMD ["ping","-c","3","localhost"], running docker run img google.com executes google.com — not a ping at all, because the whole CMD was replaced.

Exec form versus shell form is the detail that causes production bugs.

DOCKERFILE
CMD ["node", "server.js"] # exec form -> PID 1 is node
CMD node server.js # shell form -> PID 1 is /bin/sh, node is a child

Shell form wraps the command in /bin/sh -c. That shell becomes PID 1 and does not forward signals, so docker stop sends SIGTERM to the shell, your process never receives it, and after the grace period Docker sends SIGKILL. The result is no graceful shutdown: in-flight requests dropped, connections not closed, buffers not flushed. Always use exec form for the main process.

Which to choose:

  • ENTRYPOINT + CMD — the image is a tool. Arguments are what the user varies.
  • CMD only — the image runs an application and users occasionally need to run something else instead, such as a shell for debugging.
  • ENTRYPOINT only — the image takes no meaningful arguments.

Overriding. CMD is replaced by trailing arguments to docker run. ENTRYPOINT requires the explicit --entrypoint flag, which is why putting your application in ENTRYPOINT makes docker run img sh fail — a common debugging frustration.

The entrypoint script pattern handles setup that must happen before the app starts:

Shell
#!/bin/sh
set -e
wait-for-db.sh # setup
exec "$@" # exec REPLACES the shell, so the app becomes PID 1

The exec is essential. Without it the script stays PID 1 and the signal problem returns.

Real-world example

A CLI tool image where ENTRYPOINT is clearly right:

DOCKERFILE
FROM alpine:3.20
RUN apk add --no-cache curl
ENTRYPOINT ["curl"]
CMD ["--help"]
TypeScript
docker run mycurl -> curl --help
docker run mycurl -sS https://example.com -> curl -sS https://example.com

The image behaves exactly like the curl binary. Had curl been in CMD, the second command would try to execute -sS as a program and fail immediately.

Common mistakes

  • - Using shell form for the main process, so PID 1 is a shell that swallows SIGTERM and the container never shuts down gracefully.
  • - Putting the executable in CMD when the image is a tool, so any user argument replaces the command entirely.
  • - Writing an entrypoint script without `exec "$@"`, leaving the script as PID 1 and reintroducing the signal problem.
  • - Forgetting that overriding ENTRYPOINT requires `--entrypoint`, which makes `docker run img sh` fail confusingly.
  • - Mixing shell and exec form between ENTRYPOINT and CMD
  • when ENTRYPOINT is shell form, CMD is ignored entirely.
  • - Assuming the application receives SIGTERM without checking — many containers are silently SIGKILLed after the grace period.

Follow-up questions

  • Why does shell form break graceful shutdown?
  • What does `exec "$@"` do in an entrypoint script?
  • When would you use CMD alone?
  • What is a zombie-reaping init and when do you need one?

More Docker interview questions

View all →