Skip to content

CMD vs ENTRYPOINT

Docker gives you two instructions for defining what happens when a container starts:

  • CMD — the default command (or default arguments). Easily overridden at docker run.
  • ENTRYPOINT — the fixed executable. Harder to override; makes the container behave like a specific program.

They are often confused. Understanding the difference lets you build containers that are easy to use correctly.

Both CMD and ENTRYPOINT accept two syntax forms.

CMD ["node", "server.js"]
ENTRYPOINT ["node"]

Arguments are a JSON array. Docker executes the program directly — no shell involved. PID 1 inside the container is your process. This means it receives OS signals (SIGTERM, SIGINT) properly, which is critical for graceful shutdown.

CMD node server.js
ENTRYPOINT node

Docker wraps the command in /bin/sh -c "...". The shell becomes PID 1, not your process. Your app runs as a child of the shell, and OS signals are not forwarded reliably. Avoid this form for production services.

CMD sets the default command a container runs. If the user passes a command on docker run, it replaces CMD entirely.

# syntax=docker/dockerfile:1
FROM alpine:3.20
CMD ["echo", "Hello from CMD!"]
Terminal window
# Uses CMD
docker run --rm myimage
# Output: Hello from CMD!
# Overrides CMD completely
docker run --rm myimage echo "Custom message"
# Output: Custom message

ENTRYPOINT sets the executable that always runs. Arguments passed on docker run are appended to the entry point — not substituted.

# syntax=docker/dockerfile:1
FROM alpine:3.20
ENTRYPOINT ["ping"]
CMD ["-c", "3", "8.8.8.8"]
Terminal window
# Uses ENTRYPOINT + CMD defaults
docker run --rm pinger
# Runs: ping -c 3 8.8.8.8
# Appends to ENTRYPOINT, replaces CMD
docker run --rm pinger -c 1 1.1.1.1
# Runs: ping -c 1 1.1.1.1

The most flexible and idiomatic pattern:

  • ENTRYPOINT = the executable (fixed)
  • CMD = default arguments (replaceable by the user)
# syntax=docker/dockerfile:1
FROM node:22-alpine
WORKDIR /app
COPY server.js .
ENTRYPOINT ["node"]
CMD ["server.js"]
Terminal window
# Default: node server.js
docker run --rm myapp
# Override: node --version
docker run --rm myapp --version

You can still override ENTRYPOINT when needed using the --entrypoint flag:

Terminal window
docker run --rm --entrypoint sh myapp

This drops you into a shell inside the container — useful for debugging.

No overridedocker run img argsdocker run --entrypoint X img
Only CMDruns CMDreplaces CMD with argsn/a — uses new entrypoint
Only ENTRYPOINTruns ENTRYPOINTappends args to ENTRYPOINTreplaces ENTRYPOINT
ENTRYPOINT + CMDruns ENTRYPOINT + CMD defaultsreplaces CMD with argsreplaces ENTRYPOINT
# syntax=docker/dockerfile:1
FROM alpine:3.20
# ENTRYPOINT sets the fixed executable
# CMD sets the default arguments
ENTRYPOINT ["echo"]
CMD ["Hello from ENTRYPOINT + CMD!"]
# --- build ---
# docker build -t greet .
# Run with defaults (uses CMD):
# docker run --rm greet
# Override CMD with your own message:
# docker run --rm greet "My custom message"
# Override ENTRYPOINT entirely:
# docker run --rm --entrypoint sh greet
Why is exec form (`["node", "server.js"]`) preferred over shell form (`node server.js`)?
If a Dockerfile has both ENTRYPOINT ["ping"] and CMD ["-c", "3", "8.8.8.8"], what runs when you execute `docker run img -c 1 1.1.1.1`?
Which docker run flag allows you to replace ENTRYPOINT at runtime?
What is the idiomatic use of CMD when ENTRYPOINT is also set?