Mental model
A Dockerfile is a plain text recipe. docker build reads it top-to-bottom, running each instruction, saving a filesystem layer after each. The result is a reusable image you can docker run.
Cisco objective 4.6 says “Interpret contents of a Dockerfile”. You need to read a Dockerfile and explain what each line does.
The example
A Python Flask web app in 9 lines.
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 5000
ENV FLASK_APP=app.py
CMD ["flask", "run", "--host=0.0.0.0"]
Line by line
FROM python:3.11-slim
Every Dockerfile starts with FROM. It names a base image. Here, the official Python 3.11 slim image (small Debian + Python + pip). Subsequent instructions add on top of it.
WORKDIR /app
Sets the working directory for the rest of the Dockerfile. Any later COPY, RUN, CMD happens relative to /app. Creates the directory if it does not exist.
COPY requirements.txt .
Copies requirements.txt from the build context (the directory you ran docker build from) into /app/. on the image. The . here means “the current WORKDIR”.
RUN pip install --no-cache-dir -r requirements.txt
Runs a command during build. The output of this command becomes part of the next layer. --no-cache-dir keeps the layer small by not caching downloaded wheels.
COPY app.py .
Second COPY. Deliberately after requirements install: pip install is slow and does not change often. If only app.py changes, the earlier layer cache stays valid.
EXPOSE 5000
Documents which port the container listens on. Does not actually open the port at run time (you still need -p 5000:5000 on docker run); it is a hint to humans and tools.
ENV FLASK_APP=app.py
Sets an environment variable visible to the running process.
CMD ["flask", "run", "--host=0.0.0.0"]
The command to run when a container starts. CMD can be overridden by docker run ... <different command>. Prefer the JSON-array form (["cmd", "arg1", "arg2"]) to avoid shell parsing surprises.
The 10 instructions worth knowing
| Instruction | Purpose |
|---|---|
FROM | Base image. Required. |
WORKDIR | Set the working directory for subsequent instructions. |
COPY <src> <dst> | Copy from build context into image. |
ADD <src> <dst> | Like COPY but also extracts tarballs and fetches URLs. Prefer COPY unless you need those extras. |
RUN <cmd> | Execute a command during build. |
ENV KEY=value | Set an env var in the image (persists at runtime too). |
EXPOSE <port> | Document the port. |
CMD ["x", "y"] | Default command when a container starts. Overridable. |
ENTRYPOINT ["x"] | Fixed command; CMD becomes its default arguments. |
USER <name> | Run subsequent instructions / the final process as this user. Good security hygiene. |
CMD vs ENTRYPOINT
CMDis a suggestion.docker run myimage echo helloreplaces the CMD.ENTRYPOINTis a fix.docker run myimage echo hellobecomes<entrypoint> echo hellowith echo being arguments.
Common pattern:
ENTRYPOINT ["python", "-m"]
CMD ["myapp"]
Default run: python -m myapp. Override: docker run myimage pytest becomes python -m pytest.
Layer caching
Docker caches each instruction’s output. If you change line 7, lines 1 to 6 are reused instantly. Rule:
- Instructions that change rarely go first (base image, system deps).
- Instructions that change often go last (your application code).
A common blunder: COPY . . on line 2. Any file change invalidates everything after it, including the slow pip install.
The .dockerignore file
Alongside the Dockerfile. Lists files to EXCLUDE from the build context. Keeps secrets (.env, .aws/credentials) and junk (node_modules, __pycache__) out of your image.
# .dockerignore
.git
.env
__pycache__
*.pyc
node_modules
Building and running
docker build -t myapp:1.0 . # . = build context (current dir)
docker run -d -p 5000:5000 myapp:1.0
See the docker run / pull topic for the full image / container workflow.
Build an image, run a container, tear it down
12 steps: pull, run, ps, logs, exec, stop, rm, build from a tiny Dockerfile. Real engine; images and containers tracked across the whole session.
Open the lab →FAQ
Does the order of lines matter? Yes. Instructions that invalidate cache (COPY .) should come as late as possible. Order from least-changing to most-changing.
Can I have more than one FROM in a Dockerfile? Yes — a multi-stage build. First stage compiles with a big SDK image; final stage copies just the binary into a tiny runtime image. Not called out directly in 200-901.
Where does COPY . . copy from? The build context — the directory passed to docker build. If you ran docker build -t app ., the context is the current directory. Everything (minus .dockerignore) is sent to the Docker daemon.
What happens if I omit CMD? If the base image has one (most do), you inherit it. If neither has one, docker run fails because there is nothing to run.
