Chapter 02
Writing a Dockerfile
The instructions that turn source code into an image, and the details that decide how the container starts and stops.
FROM and base images
- What
FROMpicks the starting image and begins a build stage. Everything after it builds on that base, including its OS packages and default user.- Why
- The base decides image size, available tools, libc and most of the vulnerabilities you inherit.
Watch out
Using a floating base such as node or python:latest, so a rebuild months later silently gets a new major version.
RUN
- What
- Executes a command at build time and saves the filesystem changes as a new layer.
- Why
- It installs packages and compiles code. Chaining related commands in one
RUNkeeps caches and temp files out of the final layer.
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*Watch out
Running apt-get update in a separate RUN from the install: the cached update layer goes stale and installs fail or pull old versions.
COPY vs ADD
- What
COPYcopies files from the build context into the image.ADDalso fetches URLs and unpacks local tar archives. Both accept--chownto set ownership.- Why
- Prefer
COPYfor its predictable behaviour; useADDonly when you want the extraction or remote fetch.
Watch out
Copying files and then running chown -R in a later layer, which duplicates every byte; use COPY --chown instead.
WORKDIR, ENV and ARG
- What
WORKDIRsets the working directory (creating it).ENVsets variables in the image and at runtime.ARGsets build-time variables passed with--build-arg; they are gone at runtime.- Why
- They keep paths and versions in one place and let one Dockerfile build variants.
Watch out
Passing secrets as ARG or ENV: they are visible in docker history and the image config.
CMD vs ENTRYPOINT
- What
ENTRYPOINTis the executable the container always runs;CMDgives default arguments, replaced by anything after the image name indocker run. With onlyCMD, it is the whole command.- Why
- Use
ENTRYPOINTfor images that act like a single tool andCMDalone for apps where users may want to run a different command.
ENTRYPOINT ["python", "-m", "reportgen"]
CMD ["--help"]Exec form and PID 1
Also called shell form, tini, --init
- What
- Exec form
["node", "server.js"]runs the process directly as PID 1. Shell formnode server.jswraps it in/bin/sh -c, which may not forward signals.docker run --initadds a tiny init that forwards signals and reaps zombies. - Why
- If SIGTERM never reaches your app, every stop waits for the timeout and ends in SIGKILL, dropping in-flight requests.
Watch out
Starting through an entry script without exec "$@" at the end, so the script, not the app, is PID 1.
HEALTHCHECK
- What
- An instruction that runs a command inside the container on an interval. The container is reported as starting, healthy or unhealthy.
- Why
- Compose uses it to order startup, and
docker psshows at a glance whether the app actually serves, not only whether the process runs.
HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
CMD curl -fsS http://localhost:3000/health || exit 1Watch out
Relying on curl in a slim or distroless image that does not ship it; use a tiny built-in check command instead.
LABEL and OCI annotations
- What
- Key and value metadata stored in the image, such as
org.opencontainers.image.sourceandorg.opencontainers.image.revision. - Why
- Registries and scanners use these labels to link an image back to its repository and commit.