What's the difference between ENTRYPOINT and CMD?

6 minintermediatedockerfileentrypointcmd

Quick Answer

ENTRYPOINT defines the fixed, primary command a container always runs. CMD provides default arguments to that command — or, used alone with no ENTRYPOINT, defines the whole default command — and can be easily overridden at docker run time. When both are set, CMD's value is passed as arguments to ENTRYPOINT. This combination is the standard pattern for an image that behaves like a fixed executable with sensible, overridable default arguments.

Detailed Answer

CMD alone — a default command, easily overridden

CMD ["node", "server.js"]
docker run myapp                  # runs: node server.js
docker run myapp node debug.js     # OVERRIDES the entire CMD -- runs: node debug.js instead

Any arguments given after the image name on docker run completely replace the CMD. This makes CMD alone a good fit when the image should be flexible about what it runs — a general-purpose base image, or a dev image where you might want to run a shell or a different script for debugging.

ENTRYPOINT alone — a fixed command that always runs

ENTRYPOINT ["node", "server.js"]
docker run myapp                    # runs: node server.js
docker run myapp --port=9000         # runs: node server.js --port=9000 (appended as ARGS, not a replacement)

Arguments given at docker run are appended to the ENTRYPOINT, not used to replace it. Use ENTRYPOINT when the image should always run one specific thing, no matter what — it makes the container behave like a fixed, dedicated executable.

Combining both — the standard, recommended pattern

ENTRYPOINT ["node"]
CMD ["server.js"]
docker run myapp                # runs: node server.js       (CMD's default argument used)
docker run myapp debug.js        # runs: node debug.js        (CMD's default OVERRIDDEN, but still passed to ENTRYPOINT)

This gives you the best of both: ENTRYPOINT fixes what program runs — always node — while CMD provides a sensible default argument. That default is still easy to override for a one-off, without needing to override the entire command.

Exec form vs. shell form — a critical, easy-to-miss distinction

# Exec form (recommended): runs the command DIRECTLY, no shell involved
CMD ["node", "server.js"]

# Shell form: runs the command wrapped in "/bin/sh -c ..."
CMD node server.js

The exec form, using JSON array syntax, runs the program directly as PID 1 inside the container. Signals like SIGTERM, sent by docker stop, go straight to it, so it can shut down gracefully. The shell form instead runs /bin/sh -c "node server.js". The shell itself becomes PID 1, and it's the shell's job to forward signals to the actual application — a job it doesn't always do correctly. This is a common, subtle cause of containers that don't shut down gracefully: they ignore SIGTERM and only stop when docker stop's timeout forces a SIGKILL.

ScenarioRecommended setup
Fixed, purpose-built application containerENTRYPOINT + CMD (overridable default args)
General-purpose or dev image, command often replaced entirelyCMD alone
Either form, alwaysExec (JSON array) syntax, for correct signal handling