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=1Use 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 rundo 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→ runspython server.pydocker run myapp worker.py→ runspython 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.
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.
| Command | Entrypoint | Arguments |
|---|---|---|
docker run myapp | python | server.py from CMD |
docker run myapp worker.py | python | worker.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 definition | Result of docker run myapp worker.py |
|---|---|
ENTRYPOINT ["python"] plus CMD ["server.py"] | python worker.py |
CMD ["python", "server.py"] only | tries 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}-slimdocker 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.
| Property | ENV | ARG |
|---|---|---|
| Exists during build | yes | yes |
| Exists in running container | yes | no |
| Set from | Dockerfile, 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.
| Need | Instruction |
|---|---|
| pick a base version at build time | ARG |
| set a config value the app reads at runtime | ENV |
| pass a secret | neither, 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.