Command Palette

Search for a command to run...

Hectal
PHASE 2Beginner ~14 min· topic 5 of 5

Topic 2.5

ENTRYPOINT vs CMD, and ARG vs ENV

In one line

Four instructions that look interchangeable until an interview (or a 2am incident) asks you to explain the exact difference — CMD is a default you can override, ENTRYPOINT usually isn't, ARG only exists at build time, ENV lives on into the running container.

0/5 · 0%

Key ideas

  1. 01

    Real-life example for CMD vs ENTRYPOINT: CMD is like a restaurant's 'suggested side dish' — the kitchen serves it by default, but you can ask for a different one instead. ENTRYPOINT is like the restaurant's actual cooking process — you can add ingredients (arguments) to it, but you can't tell the kitchen to stop cooking and do something completely unrelated without a special request.

  2. 02

    CMD ["java", "-jar", "app.jar"] sets the DEFAULT command — docker run myimage runs exactly that, but docker run myimage --help REPLACES the entire CMD with --help, which is almost never what you want for an app image (it's ideal for a general-purpose image like python or ubuntu where the whole point IS letting the caller choose what to run).

  3. 03

    ENTRYPOINT ["java", "-jar", "app.jar"] sets the process that ALWAYS runs — docker run myimage --spring.profiles.active=prod does NOT replace it; the extra text is APPENDED as arguments to the entrypoint instead, becoming java -jar app.jar --spring.profiles.active=prod. This is the right choice for an image that has exactly one job.

  4. 04

    The genuinely powerful pattern combines both: ENTRYPOINT fixes the unchangeable command, CMD supplies DEFAULT arguments to it that a caller can still override — ENTRYPOINT ["java", "-jar", "app.jar"] plus CMD ["--spring.profiles.active=default"] means docker run myimage uses the default profile, while docker run myimage --spring.profiles.active=prod swaps in a different profile, and the underlying java -jar app.jar invocation itself can never be accidentally dropped.

  5. 05

    Overriding a container's ENTRYPOINT entirely (rather than just its CMD arguments) IS still possible, deliberately, with the explicit --entrypoint flag — e.g. docker run --entrypoint bash myimage to get a shell inside an image that would normally always launch your app. This is the standard trick for debugging an image whose ENTRYPOINT would otherwise never let you in.

  6. 06

    ARG defines a variable available ONLY during the image BUILD (inside the Dockerfile, resolved by docker build --build-arg KEY=value) — it does not exist in the final running container at all unless you deliberately copy its value into an ENV instruction. Use it for things like choosing a base image variant or a version number at build time.

  7. 07

    ENV defines a variable that's baked into the image AND remains set in every container run from it — visible to your application at runtime via normal environment variable lookups (System.getenv("KEY") in Java). Use it for actual runtime configuration, like SPRING_PROFILES_ACTIVE.

  8. 08

    A common, useful combination: ARG APP_VERSION (supplied at build time by your CI pipeline) followed by ENV APP_VERSION=$APP_VERSION — this takes a BUILD-time-only value and deliberately promotes it into a RUNTIME-visible one, so your running app can report its own version, something a bare ARG could never do.

In your stack

  • →

    A Spring Boot image built with ENTRYPOINT + a default-args CMD gives you the best of both: docker run myimage boots normally, while docker run myimage --debug or docker run myimage --spring.profiles.active=staging lets whoever's running it override JUST the arguments, without ever risking accidentally replacing the entire java -jar app.jar launch command the way a bare CMD-only image would allow.

Code & diagrams

Dockerfilemarkdown

ENTRYPOINT can't be silently dropped by a stray docker run argument; CMD's defaults still can be overridden on purpose.

ARG JAVA_VERSION=17
FROM eclipse-temurin:${JAVA_VERSION}-jre

ARG APP_VERSION=dev
ENV APP_VERSION=${APP_VERSION}

WORKDIR /app
COPY target/myapp.jar app.jar

# The command that ALWAYS runs — cannot be accidentally replaced
ENTRYPOINT ["java", "-jar", "app.jar"]

# Default ARGUMENTS to the entrypoint — CAN be overridden
CMD ["--spring.profiles.active=default"]
run-examples.shmarkdown

Same image, three different results — because of exactly how ENTRYPOINT and CMD interact.

# Uses the default CMD args -> java -jar app.jar --spring.profiles.active=default
docker run myimage

# Overrides ONLY the CMD args -> java -jar app.jar --spring.profiles.active=prod
docker run myimage --spring.profiles.active=prod

# Builds with a different ARG value baked in at build time
docker build --build-arg APP_VERSION=1.4.2 -t myimage .

# Bypasses ENTRYPOINT entirely, on purpose, to get a debugging shell
docker run --entrypoint bash myimage

Explain it without notes

01

Why does docker run myimage --help behave completely differently depending on whether the image used CMD alone versus ENTRYPOINT alone for its launch command?

02

Why can't you read an ARG's value from inside your running application, even though it was clearly used during the build?

Practice

01

Build one image using ENTRYPOINT and a separate one using CMD for the exact same launch command, then run both with an extra trailing argument and compare the actual behavior.

02

Add an ARG for a version number to a Dockerfile, build it with --build-arg, and try (and fail) to read it from inside the running container — then fix it by promoting it to an ENV instead.

Trade-offs

  • ↔

    ENTRYPOINT-only images are safer for single-purpose app images (nobody can accidentally 'run' something unrelated), but are less flexible for general-purpose tool images (like a base ubuntu or python image) where CMD-only, letting the caller specify absolutely anything, is exactly the intended design.

Done when you can

  • I can predict exactly what docker run myimage <extra args> does for an image using ENTRYPOINT+CMD together.

  • I know --entrypoint overrides an ENTRYPOINT deliberately when I need to debug inside an otherwise-fixed image.

  • I understand ARG values vanish after build unless explicitly promoted into an ENV.