Course outline · 0% complete

0/29 lessons0%

Course overview →

ENTRYPOINT, ENV, and ARG

lesson 3-4 · ~11 min · 10/29

The instructions real Dockerfiles add

Open the Dockerfile of almost any serious open-source project and you will meet three instructions lesson 3-1 skipped: ENV, ENTRYPOINT, and ARG. They exist because images need configuration at three different moments: values baked in for every run, a fixed command with swappable arguments, and values that exist only while building. Confusing them produces two classic bugs: config that mysteriously will not change, and "why is my command-line flag being ignored".

ENV sets an environment variable inside the image, available during the rest of the build and in every container started from it:

ENV PYTHONUNBUFFERED=1

Use it for settings that should hold everywhere the image runs (this one makes Python print logs immediately instead of buffering them, so docker logs shows output live). Per-machine config like DATABASE_URL still comes at run time via -e or compose environment: (lesson 5-2), because baking a machine-specific value into the image would break the run-anywhere promise.

ENTRYPOINT vs CMD

Both say what runs when a container starts, and the real rule is about overriding:

  • ENTRYPOINT is the fixed part of the command. Arguments after the image name in docker run do not replace it
  • CMD is the default arguments. Anything after the image name replaces CMD entirely

Docker starts the container with ENTRYPOINT + CMD glued together:

ENTRYPOINT ["python"]
CMD ["server.py"]
  • docker run myapp → runs python server.py
  • docker run myapp worker.py → runs python worker.py

The image now behaves like a command with a default argument. This is why docker run python:3.12 python in lesson 2-1 could swap the command freely (that image relies on CMD), while images built as single-purpose tools use ENTRYPOINT so users can pass just arguments. When only one of the two is set, CMD alone (lesson 3-1's shape) means the whole command is replaceable.

docker run myapp ENTRYPOINT python CMD server.py python server.py docker run myapp worker.py ENTRYPOINT python argument worker.py python worker.py
How Docker assembles the start command. ENTRYPOINT is the fixed prefix and CMD supplies default arguments, so anything after the image name replaces only the arguments.

Assembling the start command

A simulation of Docker gluing the two instructions together at container start.

entrypoint="python"
cmd="server.py"
echo "docker run myapp           -> $entrypoint $cmd"
override="worker.py"
echo "docker run myapp worker.py -> $entrypoint $override"

Output

docker run myapp           -> python server.py
docker run myapp worker.py -> python worker.py

The first line is a plain docker run, which uses the entrypoint plus the default arguments. The second passes an extra argument, which replaces the CMD part and never touches the entrypoint.

CommandEntrypointArguments
docker run myapppythonserver.py from CMD
docker run myapp worker.pypythonworker.py from the command line

The entrypoint is the constant in both rows, which is the entire distinction between the two instructions.

Overriding CMD but not ENTRYPOINT

With ENTRYPOINT ["python"] and CMD ["server.py"], the command docker run myapp worker.py executes python worker.py inside the container.

Arguments after the image name replace CMD and leave ENTRYPOINT alone, so the assembled command is the entrypoint plus your arguments.

Image definitionResult of docker run myapp worker.py
ENTRYPOINT ["python"] plus CMD ["server.py"]python worker.py
CMD ["python", "server.py"] onlytries to execute worker.py directly, and likely fails

The second row is the failure people hit without knowing why. With only a CMD, the whole command is replaceable, so the shell inside the container receives a Python file as if it were a program.

ARG: variables that exist only at build time

ARG declares a variable for the build itself, set from the command line:

ARG PY_VERSION=3.12
FROM python:${PY_VERSION}-slim
docker build --build-arg PY_VERSION=3.13 -t myapp:py13 .

One Dockerfile now builds against either Python version, which is exactly how teams test an upgrade before committing to it.

The key difference from ENV is lifetime. An ARG evaporates when the build finishes and is not present in running containers, which also makes it the wrong place for secrets, because build args can still be recovered from image metadata. Real secret handling arrives in lesson 8-2.

PropertyENVARG
Exists during buildyesyes
Exists in running containeryesno
Set fromDockerfile, or -e at run--build-arg at build

The build-only variable instruction

The instruction is ARG.

It is set with --build-arg on the docker build command line, and it parameterizes the build itself, covering things like base versions and target platforms. It evaporates when the build ends, while ENV values persist into every container started from the image.

NeedInstruction
pick a base version at build timeARG
set a config value the app reads at runtimeENV
pass a secretneither, see lesson 8-2

Neither one is a safe home for a secret, since both end up recorded in image metadata that anyone with the image can read.