What is the Difference Between CMD and ENTRYPOINT
Both ENTRYPOINT and CMD define the command executed when a container starts (docker run), but they fulfill fundamentally different responsibilities:
🟢 Junior Level
30-Second Interview Answer
Both ENTRYPOINT and CMD define the command executed when a container starts (docker run), but they fulfill fundamentally different responsibilities:
ENTRYPOINT: Defines the fixed, immutable executable binary of the container. It transforms the container into a standalone executable tool (e.g.,java). Overriding it requires an explicit--entrypointflag.CMD: Supplies default arguments to theENTRYPOINTprogram. It is designed to be easily overridden by passing CLI arguments at the end ofdocker run.
Real-World Analogy: Function Call in Code
Think of container invocation as a method call in Java:
java -jar app.jar --server.port=8080
▲ ▲ ▲
│ │ │
[ ENTRYPOINT ] [ CMD ]
(Function / Binary) (Default Arguments)
Combined Best-Practice Pattern
# Fixed executable binary and base parameter:
ENTRYPOINT ["java", "-jar", "app.jar"]
# Default overridable parameter:
CMD ["--server.port=8080"]
CLI Execution Scenarios:
- Default startup:
docker run order-service # Resulting command: java -jar app.jar --server.port=8080 - Overriding default arguments at runtime:
docker run order-service --server.port=9090 # Resulting command: java -jar app.jar --server.port=9090 (CMD is replaced completely!)
🟡 Middle Level
Syntactic Forms: Exec Form vs Shell Form
| Dimension | Exec Form (Industry Standard) | Shell Form (Anti-Pattern) |
|---|---|---|
| Syntax | ENTRYPOINT ["java", "-jar", "app.jar"] |
ENTRYPOINT java -jar app.jar |
| Execution Mechanism | Directly invoked via kernel system call execve() |
Wrapped implicitly in /bin/sh -c "..." |
| Process Hierarchy | Application runs directly as PID 1 | Shell runs as PID 1, Java runs as PID 2 |
| Signal Handling | Kernel signals (SIGTERM) delivered to Java |
Shell intercepts and drops SIGTERM |
| Graceful Shutdown | 🟢 Works correctly (Spring shutdown hooks run) | ❌ Broken (Killed abruptly with SIGKILL after 30s) |
Interaction Matrix: ENTRYPOINT and CMD Combinations
| Dockerfile Configuration | CLI Command | Resulting Command Executed by Kernel |
|---|---|---|
CMD ["java", "-jar", "app.jar"] |
docker run my-app |
java -jar app.jar |
CMD ["java", "-jar", "app.jar"] |
docker run my-app bash |
bash (CLI completely replaces CMD) |
ENTRYPOINT ["java", "-jar", "app.jar"] |
docker run my-app |
java -jar app.jar |
ENTRYPOINT ["java", "-jar", "app.jar"] |
docker run my-app --debug |
java -jar app.jar --debug (Appended) |
ENTRYPOINT ["java"] + CMD ["-version"] |
docker run my-app |
java -version |
ENTRYPOINT ["java"] + CMD ["-version"] |
docker run my-app -jar app.jar |
java -jar app.jar (Replaces CMD) |
Kubernetes Pod Specification Mapping
When translating container definitions into Kubernetes manifests, Docker’s terminology is mapped differently:
| Kubernetes Pod Spec Field | Corresponding Dockerfile Directive |
|---|---|
command: |
ENTRYPOINT |
args: |
CMD |
apiVersion: v1
kind: Pod
metadata:
name: payment-service
spec:
containers:
- name: app
image: payment-service:1.0
command: ["java"] # Overrides image ENTRYPOINT
args: ["-jar", "/app.jar", "--spring.profiles.active=k8s"] # Overrides image CMD
[!WARNING] In Kubernetes, specifying the
commandfield completely wipes out both theENTRYPOINTandCMDfrom the original Dockerfile (unlessargsis also explicitly specified).
🔴 Senior Level
The PID 1 Problem & Kernel Signal Immunity
In UNIX systems, PID 1 (init or systemd) has unique kernel privileges and responsibilities:
- Signal Immunity: The Linux kernel does not apply default signal handlers to PID 1. If PID 1 does not install an explicit signal handler for
SIGTERM, the kernel ignores the signal. - Why Shell Form Breaks Graceful Shutdown:
- With
ENTRYPOINT java -jar app.jar,/bin/shruns as PID 1, and the JVM runs as a child process (PID 2). - When Kubernetes terminates a Pod (
kubectl delete pod), the Kubelet dispatchesSIGTERMto the container’s PID 1 (/bin/sh). /bin/shreceivesSIGTERMbut does not propagate it to child processes.- The JVM never receives notification to shut down. Active HTTP requests are not drained, transactions in progress are not committed, and connection pools are not closed.
- When
terminationGracePeriodSeconds(default: 30s) expires, the kernel issues an uncatchableSIGKILL(Exit Code 137), brutally terminating the JVM and potentially corrupting data.
- With
The Entrypoint Wrapper Pattern & POSIX exec "$@"
Enterprise applications often require pre-startup operations: fetching dynamic secrets from HashiCorp Vault, setting up file permissions, or running database migrations. This is implemented using an entrypoint script:
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
# Script acts as entrypoint; CMD supplies the Java binary command
ENTRYPOINT ["/entrypoint.sh"]
CMD ["java", "-jar", "app.jar"]
#!/bin/sh
# entrypoint.sh
set -e
echo "Initializing environment, secrets, and database certificates..."
# ⚠️ CRITICAL: Must use the POSIX 'exec' command!
exec "$@"
Why exec "$@" is Mandatory
The shell built-in exec triggers the kernel system call execve(2):
- It replaces the current shell process image with the target binary (
java), retaining the exact same PID 1. - If you execute
"$@"withoutexec, the shell remains PID 1, spawns Java as PID 2, and the signal forwarding problem returns!
Zombie Process Reaping & Init Systems (Tini)
If a containerized application spawns child processes (via Java’s ProcessBuilder or OS utilities):
- When a child process terminates, it becomes a Zombie (
[defunct]) until its parent reaps its exit code viawaitpid(2). - If the parent process exits before its children, the orphans are reparented to PID 1.
- If the root container process (like a custom Java runtime) does not reap orphaned zombies, they permanently occupy slots in the host kernel process table, eventually causing PID Exhaustion.
- Solution: Use a lightweight init manager such as Tini:
ENTRYPOINT ["/usr/bin/tini", "--", "java", "-jar", "app.jar"](Or run containers with the Docker flag:
docker run --init).
4 Tricky Questions with In-Depth Answers
1. In a Kubernetes manifest, if you configure only command: ["java"] without specifying args, what happens to CMD from the original Dockerfile?
Answer:
The CMD parameters from the Dockerfile are completely discarded!
According to the Kubernetes container runtime specification:
- If
commandis set, it overrides the DockerENTRYPOINT. - If
argsis omitted, Kubernetes sets the argument array to empty ([]), entirely ignoring any defaultCMDdeclared in the underlying Dockerfile. - As a result, the container executes
javawith no arguments, prints JVM usage information to stdout, and immediately exits with code 1 or 0, landing the Pod in aCrashLoopBackOffstate.
2. What happens if ENTRYPOINT is defined in Shell Form and CMD is defined in Exec Form? Are arguments from CMD passed to the application?
Answer:
No, the CMD arguments are completely ignored.
Consider this configuration:
ENTRYPOINT java -jar app.jar
CMD ["--server.port=8080"]
Docker compiles the shell form into /bin/sh -c "java -jar app.jar". Under POSIX shell conventions, parameters passed after /bin/sh -c "command" are treated as arguments for the shell itself ($0, $1), not the command string inside quotes. Consequently, the Docker daemon never passes the CMD array or any docker run trailing arguments to java -jar app.jar. The application runs strictly with no arguments.
3. In an entrypoint wrapper script, why is exec "$@" written with double quotes and an at-sign rather than exec $*?
Answer: The distinction lies in shell Word Splitting:
$*(unquoted): Expands all arguments as a single flat string and splits them on whitespace characters (IFS). If an argument contains spaces—such as a JVM property-Dapp.name="Payment Service"—it gets improperly fragmented into two invalid tokens:-Dapp.name="PaymentandService"."$@"(double-quoted with at-sign): Preserves each individual argument exactly as passed, retaining embedded spaces and special characters. Combined withexec, it replaces the shell process with the target command under PID 1 with pristine argument integrity.
4. Can you pass JVM memory parameters (e.g. -Xmx1g) via CMD if ENTRYPOINT is declared as ["java", "-jar", "app.jar"]?
Answer:
No, doing so will cause the JVM to ignore the setting or fail!
The syntax of the java binary requires VM options to be specified before the -jar option:
java [options] -jar filename [args...].
If ENTRYPOINT is fixed as ["java", "-jar", "app.jar"], any arguments supplied by CMD are appended after app.jar:
java -jar app.jar -Xmx1g.
The JVM treats -Xmx1g as an application argument passed to public static void main(String[] args), not as a heap configuration flag!
Correct approach: Inject JVM flags using the JAVA_TOOL_OPTIONS environment variable (ENV JAVA_TOOL_OPTIONS="-Xmx1g"), or declare ENTRYPOINT ["java"] with CMD ["-jar", "app.jar"].
🎯 Interview Cheat Sheet
Architectural Comparison
| Aspect | ENTRYPOINT | CMD |
|—|—|—|
| Core Responsibility | Fixed executable binary / command | Default overridable parameters |
| CLI Override Difficulty | Hard (requires --entrypoint /bin/sh) | Easy (docker run my-image arg1 arg2) |
| Kubernetes Equivalent | command: | args: |
| Mandatory Format | Exec Form ["binary", "arg"] | Exec Form ["param1", "param2"] |
| Signal Behavior | Application is PID 1; receives SIGTERM | Appended to ENTRYPOINT |
Production Golden Rules
- Always use Exec Form: Never use string shell form (
ENTRYPOINT java ...); it breaks graceful shutdown by running behind an unresponsive/bin/shPID 1. - Standard Microservice Pattern: Use
ENTRYPOINT ["java", "-jar", "app.jar"]for the fixed command andCMD ["--server.port=8080"]for default arguments. - Always use
exec "$@"in Wrapper Scripts: Ensures the target process replaces the shell and maintains PID 1. - Never put JVM heap flags in
CMD: UseJAVA_TOOL_OPTIONSinstead.
Red Flags (DO NOT Say)
- ❌ “ENTRYPOINT and CMD are synonyms; it doesn’t matter which one you use.” (Fails basic understanding of container execution lifecycle and argument overriding).
- ❌ “Shell form is safe and makes no difference.” (Shell form breaks OS signal handling, preventing graceful shutdown and forcing
SIGKILL). - ❌ “In Kubernetes,
commandcorresponds toCMDandargscorresponds toENTRYPOINT.” (It is the exact opposite:command=ENTRYPOINT,args=CMD).
Related Questions
- What are the Main Instructions Used in Dockerfile — Comprehensive Dockerfile syntax
- What is Pod in Kubernetes — How Kubernetes runs container workloads
- How to Organize Rolling Update in Kubernetes — Graceful termination during deployment rollouts
- What is Dockerfile — Dockerfile fundamentals and layer caching