Question 5 · Section 14

What is the difference between CMD and ENTRYPOINT?

Both instructions define what will run when the container starts, but they have different roles:

Language versions: English Russian Ukrainian

Junior Level

Simple Explanation

Both instructions define what will run when the container starts, but they have different roles:

  • ENTRYPOINT — the main program of the container (e.g., java)
  • CMD — default arguments for that program (e.g., -jar app.jar)

RUN — executes during image BUILD. CMD/ENTRYPOINT — at container LAUNCH.

Analogy

Think of a function call in programming:

  • ENTRYPOINT — the function name
  • CMD — the default parameters
java -jar app.jar
^^^^^ ^^^^^^^^^^^^
 |        |
ENTRYPOINT  CMD

How They Work Together

ENTRYPOINT ["java", "-jar", "/app.jar"]
CMD ["--server.port=8080"]

Running docker run myapp executes: java -jar /app.jar --server.port=8080

Running docker run myapp --server.port=9090 executes: java -jar /app.jar --server.port=9090

Two Forms of Writing

Exec form (recommended) — as a JSON array:

ENTRYPOINT ["java", "-jar", "/app.jar"]

Shell form — as a plain string:

ENTRYPOINT java -jar /app.jar

Always use Exec form — it correctly handles shutdown signals.

What to Remember

  • ENTRYPOINT — fixed command, CMD — default parameters
  • They can be used together
  • CMD is easy to override at launch, ENTRYPOINT — not
  • Always use Exec form ["cmd", "arg1", "arg2"]
  • Only one CMD and one ENTRYPOINT can exist in a Dockerfile

Middle Level

Detailed Behavior

ENTRYPOINT

Defines the main executable of the container:

  • Stability: hard to accidentally override (requires --entrypoint flag)
  • Purpose: turns the container into an “executable binary”
  • Arguments: all arguments passed to docker run are appended to the end of the ENTRYPOINT command

CMD

Defines the default command or parameters for ENTRYPOINT:

  • Flexibility: very easy to override. Any arguments at the end of docker run completely replace CMD
  • Purpose: provide standard behavior that the user can easily change

Three Usage Scenarios

Scenario 1: CMD only

CMD ["java", "-jar", "/app.jar"]

docker run myappjava -jar /app.jar docker run myapp echo helloecho hello (CMD completely replaced)

Scenario 2: ENTRYPOINT only

ENTRYPOINT ["java", "-jar", "/app.jar"]

docker run myappjava -jar /app.jar docker run myapp --debugjava -jar /app.jar --debug

Scenario 3: ENTRYPOINT + CMD (recommended)

ENTRYPOINT ["java", "-jar", "/app.jar"]
CMD ["--server.port=8080"]

docker run myappjava -jar /app.jar --server.port=8080 docker run myapp --server.port=9090java -jar /app.jar --server.port=9090

Scenario 3 (ENTRYPOINT + CMD) — recommended. Scenario 1 — for simple scripts. Scenario 2 — when the container should act like an “executable file”.

Typical Mistakes

Mistake Consequence How to avoid
Shell form ENTRYPOINT java -jar Signals don’t reach the application Exec form ["java", "-jar"]
Multiple CMDs in Dockerfile Only the last one counts One CMD per file
CMD without ENTRYPOINT CMD is easy to accidentally replace Use ENTRYPOINT + CMD pair
ENTRYPOINT without CMD Developer can’t override for debugging Add CMD with default parameters
Confusion with override Container runs the wrong thing Understand that docker run myapp arg replaces CMD

Override at Launch

# Override CMD — just pass arguments
docker run myapp --custom-arg

# Override ENTRYPOINT — use flag
docker run --entrypoint /bin/bash myapp

Shell form vs Exec form: Critical Nuance

Exec form — command runs directly as a process with PID 1. Correctly handles shutdown signals. Important for Kubernetes and Docker Stop.

Shell form — command runs as a subprocess of /bin/sh -c. OS signals arrive at the sh shell, not the application. The application may be “killed” hard without completing transactions.

What to Remember

  • Use Exec form for both instructions
  • ENTRYPOINT for the immutable part of the command
  • CMD for parameters that may change
  • Only one CMD and one ENTRYPOINT are considered (the last ones in the file)
  • Without proper signal handling, the application can’t shut down correctly

When NOT to Use ENTRYPOINT

Don’t use ENTRYPOINT if the image is intended for different commands (e.g., a curl/wget image for various requests). In this case CMD is more flexible.


Senior Level

Architectural Design of ENTRYPOINT and CMD

The difference between ENTRYPOINT and CMD is not just syntax — it’s a design pattern for creating reusable container images.

“Executable Container” Pattern

ENTRYPOINT turns a container into an executable:

ENTRYPOINT ["java"]
CMD ["-jar", "/app.jar"]

The container behaves like a binary:

docker run myapp -version          # java -version
docker run myapp -jar other.jar    # java -jar other.jar

PID 1 Problem and Signal Handling

In Linux, a process with PID 1 has special behavior:

  • Doesn’t receive signals by default (ignores SIGTERM)
  • Responsible for “adopting” orphaned processes
  • Doesn’t automatically terminate on unhandled signals

PID 1 — the main process in the container. If it can’t terminate correctly (handle SIGTERM), the application will be “killed” hard via SIGKILL.

Shell form problem:

ENTRYPOINT java -jar /app.jar

Processes in the container:

PID 1: /bin/sh -c "java -jar /app.jar"
PID 7: java -jar /app.jar

SIGTERM arrives at /bin/sh, which doesn’t know what to do with java. Result: docker stop waits 10 seconds and sends SIGKILL. The application doesn’t perform graceful shutdown — transactions are interrupted, connections aren’t closed.

Solutions:

  1. Exec form (recommended):
    ENTRYPOINT ["java", "-jar", "/app.jar"]
    

    Application is PID 1, receives signals directly.

  2. Tini (init system):
    ENTRYPOINT ["tini", "--", "java", "-jar", "/app.jar"]
    

    Tini — minimal init, correctly forwards signals and reaps zombie processes.

  3. Docker –init flag:
    docker run --init myapp
    

Interaction with Kubernetes

In Kubernetes, ENTRYPOINT and CMD map to container fields:

Docker Kubernetes
ENTRYPOINT command
CMD args
containers:
- name: app
  image: myapp
  command: ["java", "-jar"]     # overrides ENTRYPOINT
  args: ["--server.port=9090"]   # overrides CMD

Important: if command and args are specified in the Kubernetes manifest, they completely replace Docker’s ENTRYPOINT and CMD.

Trade-offs

Approach Plus Minus
CMD only Flexibility for developer Easy to accidentally replace
ENTRYPOINT only Stability Hard to override for debugging
ENTRYPOINT + CMD Best of both Need to understand interaction
Entrypoint script Flexible initialization Another layer, needs maintenance
Tini Correct signals + zombie reap Additional dependency

Edge Cases

  • Override in Kubernetes: command in K8s manifest completely replaces ENTRYPOINT. If the developer expects an entrypoint script to run migrations, but K8s overrode command — migrations won’t start.
  • ENTRYPOINT inheritance: if the base image has an ENTRYPOINT and the child Dockerfile doesn’t, the parent’s ENTRYPOINT is inherited. This can lead to unexpected behavior.
  • JSON array parsing: ENTRYPOINT ["java", "-jar", "/app.jar"] — this is JSON. An error in quotes or commas will cause the build to fail.
  • CMD as shell form + ENTRYPOINT as exec form: ENTRYPOINT ["java"] + CMD -jar app.jar — CMD runs as /bin/sh -c "java -jar app.jar", ENTRYPOINT is ignored.

“Entry Point Script” Pattern

For complex applications, an entrypoint script is used:

COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["java", "-jar", "/app.jar"]
#!/bin/sh
# docker-entrypoint.sh

# Run migrations
if [ "$RUN_MIGRATIONS" = "true" ]; then
    java -jar /app.jar --migrate
    exit 0
fi

# Launch the application (exec replaces the script process)
exec "$@"

exec "$@" replaces the shell script process with the target command, preserving PID 1 and signals. Without exec the script remains PID 1 and signals don’t reach the application.

Antipatterns

  1. Shell form without signal handling: ENTRYPOINT java -jar /app.jar — graceful shutdown doesn’t work.
  2. Multiple CMD/ENTRYPOINT: only the last one counts.
  3. ENTRYPOINT without CMD in developer images: impossible to override for debugging.
  4. Entrypoint script without exec: script remains PID 1, signals are lost.
  5. Secrets in CMD/ENTRYPOINT: CMD ["java", "-jar", "app.jar", "--db-password=secret"] — visible in docker inspect and ps.

Performance

Approach Shutdown time Graceful shutdown Zombie reap
Shell form 10s (SIGKILL timeout) No No
Exec form < 1s (SIGTERM) Yes No
Exec form + tini < 1s Yes Yes
Docker –init < 1s Yes Yes

Summary

  • Use Exec form for both instructions — critical for signal handling.
  • ENTRYPOINT + CMD together create a flexible and reusable image.
  • Understand the mapping to Kubernetes command/args.
  • The PID 1 problem is real: use exec form or tini.
  • Entrypoint scripts with exec "$@" — standard for complex initialization scenarios.
  • Only one CMD and one ENTRYPOINT instruction can exist in a Dockerfile (the last one counts).

Interview Cheat Sheet

Must know:

  • ENTRYPOINT — fixed command (PID 1), CMD — default parameters
  • ENTRYPOINT + CMD together — recommended pattern (“executable container”)
  • Exec form ["cmd", "arg"] is required for correct signal handling
  • Docker CMD is easy to override (docker run image args), ENTRYPOINT — via --entrypoint
  • In K8s: Docker ENTRYPOINT → command, CMD → args (complete replacement)
  • PID 1 problem: process without signal handler can’t do graceful shutdown
  • PID 1 solution: exec form, tini, or docker run --init

Frequent follow-up questions:

  • “What happens with docker run myapp arg?”arg replaces CMD, appended to ENTRYPOINT
  • “Why is shell form dangerous in K8s?” — Signals arrive at /bin/sh, not the application; graceful shutdown doesn’t work
  • “What does exec "$@" do in an entrypoint script?” — Replaces the script process with the target command, preserving PID 1
  • “Can you have multiple CMDs in a Dockerfile?” — Yes, but only the last one counts

Red flags (DO NOT say):

  • “CMD and ENTRYPOINT are the same thing” (different semantics and override behavior)
  • “I use shell form for convenience” (graceful shutdown is broken)
  • “In K8s command and args supplement Docker ENTRYPOINT/CMD” (they completely replace!)
  • “PID 1 doesn’t matter in a container” (critical for signal handling)

Related topics:

  • [[What are the main instructions used in Dockerfile]] — all Dockerfile instructions
  • [[What is Pod in Kubernetes]] — containers in K8s (signal handling is important)
  • [[How to organize rolling update in Kubernetes]] — graceful shutdown during updates