Choose Docker CMD and ENTRYPOINT by override behavior
CMD and ENTRYPOINT both influence the process a container starts, but they answer different questions. ENTRYPOINT defines the executable identity; CMD supplies defaults that an operator can replace. Most produ…
CMD and ENTRYPOINT both influence the process a container starts, but they answer different questions. ENTRYPOINT defines the executable identity; CMD supplies defaults that an operator can replace. Most production mistakes come from choosing syntax without testing the override contract.
Model the final argument vector
For an image intended to behave like one executable, use exec-form instructions:
FROM alpine:3.20
COPY report /usr/local/bin/report
ENTRYPOINT ["/usr/local/bin/report"]
CMD ["--format", "json"]
Running the image without extra arguments starts /usr/local/bin/report --format json. Arguments after the image name replace CMD while preserving the exec-form entrypoint:
docker run --rm report-image --format text
That becomes /usr/local/bin/report --format text. Current Docker documentation describes CMD as a default command or parameters and ENTRYPOINT as the command that runs when the container starts. Only the last CMD and last ENTRYPOINT in a build stage take effect.
When CMD alone is the better interface
FROM alpine:3.20
CMD ["sleep", "30"]
This says the entire command is a default. docker run image echo ready replaces it with echo ready. That is useful for general-purpose base or debugging images where operators are expected to choose a different executable.
Do not assume a platform’s base image has no entrypoint. Inspect the built image:
docker image inspect report-image \
--format '{{json .Config.Entrypoint}} {{json .Config.Cmd}}'
A child Dockerfile inherits configuration unless it replaces it, and setting ENTRYPOINT can reset inherited command assumptions. Pin the base image and test after dependency updates.
Exec form, shell form, and signal handling
JSON exec form does not invoke a shell automatically:
ENTRYPOINT ["/usr/local/bin/report"]
CMD ["--format", "json"]
Shell form does invoke shell processing and changes quoting, environment expansion, argument combination, and signal delivery:
ENTRYPOINT /usr/local/bin/report --format json
Docker’s current reference warns that shell-form ENTRYPOINT prevents normal CMD argument use and starts the executable as a child of /bin/sh -c. That shell can become PID 1, so the application may not receive termination signals directly. Prefer exec form for long-running services.
If an entrypoint script is necessary, validate input and replace the shell with the service:
#!/bin/sh
set -eu
# bounded setup only
exec /usr/local/bin/report "$@"
The final exec gives the service PID 1. The service must still handle termination and reap child processes where relevant; an init process may be appropriate for multi-process behavior.
Override deliberately during diagnosis
Arguments replace CMD. To replace the entrypoint itself, use the runtime’s explicit override:
docker run --rm --entrypoint /bin/sh report-image
Docker documents that --entrypoint replaces the image’s configured entrypoint. Treat this as a diagnostic escape hatch, not the normal production interface. It can bypass initialization, privilege dropping, certificate setup, or safety checks performed by an entrypoint script.
A verification matrix before release
| Run | What it proves |
|---|---|
| No arguments | Documented defaults start successfully. |
| Replacement arguments | CMD is replaceable and the entrypoint retains its identity. |
--entrypoint diagnostic | The image can be inspected without claiming the normal startup path works. |
| Stop with a short timeout | PID 1 receives termination and exits within the platform grace period. |
| Read-only filesystem/non-root | Startup does not depend on hidden mutation or elevated privilege. |
docker run --name report-test report-image
docker stop --time 10 report-test
docker inspect report-test --format '{{.State.ExitCode}}'
Also validate logs, health checks, configuration errors, and orchestration overrides. Kubernetes command and args map conceptually to image entrypoint and command fields, but test the exact workload manifest instead of translating by memory.
Failure modes and recovery
- Runtime flags are ignored: a shell-form entrypoint may be swallowing or reinterpreting them. Convert to exec form or forward
"$@"correctly. - The container ignores stop: inspect PID 1 and remove a wrapper that failed to
exec. - The binary disappears when arguments are supplied: the image probably used
CMDalone; supplying arguments replaced the whole command. - An inherited image stops working: compare
.Config.Entrypointand.Config.Cmdbefore and after the base-image change. - Quoting behaves differently: exec form performs no automatic shell expansion. Pass literal arguments or invoke a reviewed shell script explicitly.
Rollback by redeploying the previous immutable image digest, not by editing a running container. Preserve the failed container’s inspect output and logs. If only the workload override is wrong, restore the previous manifest and confirm the actual process arguments before closing the incident.
Production trade-offs
A fixed entrypoint creates a clear appliance-like contract and reduces accidental replacement of the service binary. A replaceable CMD makes a general-purpose image flexible. Entrypoint scripts enable setup but add parsing, signal, privilege, and observability responsibilities. Keep the interface narrow, document which arguments are supported, and make invalid combinations fail loudly.
Choose the instruction by what operators are allowed to replace, then test that promise at runtime.
Attribution and verification
This guide was prompted by Golo Roden’s Stack Overflow question and creack’s accepted answer, used under CC BY-SA. Current semantics were independently verified against Docker’s official CMD reference, ENTRYPOINT reference, and runtime entrypoint override documentation.
Primary source: Review the official reference ↗