AtomLearnAtomLearn
DashboardGoalsPathAchievementsReviewSign In
DashboardDocker & ContainersWriting Dockerfiles
Docker & ContainersNot Started

Writing Dockerfiles

Build custom images with FROM, RUN, COPY, CMD, ENTRYPOINT, and layer caching

0%
Knowledge0%
Learn & Drill
Fluency0%
Drill & Speed
Retention0%
Mastery & Review
Confidence0%
All modes
Practice
Knowledge
Fluency
Retention

Knowledge Debt detected

You can study this freely — but your score may plateau if these foundations have gaps. The Mastery badge requires them to be solid.

Explanation

A Dockerfile is a text file with instructions to build your own Docker image layer by layer.

Minimal Python app Dockerfile:

dockerfile # Base image — which OS and runtime to start from FROM python:3.12-slim # Set working directory inside the container WORKDIR /app # Copy dependency list first (for cache efficiency) COPY requirements.txt . # Install dependencies RUN pip install --no-cache-dir -r requirements.txt # Copy the rest of the source code COPY . . # What to run when the container starts CMD ["python", "main.py"]

Build it: docker build -t myapp:latest . Run it: docker run myapp:latest

Key instructions:

| Instruction | Purpose | |---|---| | FROM | Base image (always first) | | WORKDIR | Set current dir (creates if missing) | | COPY src dest | Copy files from host into image | | RUN cmd | Execute shell command during build | | ENV KEY=value | Set environment variable | | EXPOSE port | Document which port the app uses | | CMD ["arg"] | Default command when container starts | | ENTRYPOINT ["cmd"] | Fixed executable (CMD provides default args) |

CMD vs ENTRYPOINT:

dockerfile # CMD — can be overridden with docker run ... <command> CMD ["python", "main.py"] # docker run myapp python other.py → runs other.py # ENTRYPOINT — cannot be overridden (only args can change) ENTRYPOINT ["python"] CMD ["main.py"] # docker run myapp other.py → runs: python other.py

Layer caching — order matters:

dockerfile # WRONG: copies everything first — pip install runs on every code change COPY . . RUN pip install -r requirements.txt # RIGHT: copy requirements first, install, then copy code COPY requirements.txt . RUN pip install -r requirements.txt COPY . . # cache miss here on code changes — pip step is still cached `` Docker caches each layer. A cache miss invalidates all subsequent layers. Put slow, rarely-changing steps (installs) before fast, frequently-changing steps (source code).

Multi-stage builds — keep production images small:

dockerfile # Stage 1: builder (has compilers, dev tools) FROM python:3.12 AS builder WORKDIR /build COPY requirements.txt . RUN pip install --target=/install -r requirements.txt # Stage 2: runtime (slim, no build tools) FROM python:3.12-slim COPY --from=builder /install /usr/local/lib/python3.12/site-packages COPY . /app WORKDIR /app CMD ["python", "main.py"]

Always use .dockerignore:

# .dockerignore __pycache__/ *.pyc .git/ .env node_modules/ Without this, COPY . . sends your entire repo (including .git`) to the Docker build context.

Examples

Production-ready Dockerfile for a FastAPI app

Separating COPY requirements.txt / RUN pip from COPY . . means pip only reruns when requirements.txt changes, not on every code edit

# Dockerfile
FROM python:3.12-slim

WORKDIR /app

# Install deps separately for cache efficiency
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

# Use uvicorn as the ENTRYPOINT; CMD provides default args
ENTRYPOINT ["uvicorn", "main:app"]
CMD ["--host", "0.0.0.0", "--port", "8000"]

# .dockerignore
# __pycache__/
# .env
# .git/
# tests/

Multi-stage build: Go app with tiny final image

Multi-stage builds are the idiomatic way to ship small production images; only the final stage is deployed

# Stage 1: compile
FROM golang:1.22 AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download                # cache dep download separately
COPY . .
RUN CGO_ENABLED=0 go build -o /app ./cmd/server

# Stage 2: runtime — scratch has NO OS, just the binary
FROM scratch
COPY --from=builder /app /app
ENTRYPOINT ["/app"]

# Final image: ~5 MB vs ~900 MB for the builder stage

How well did you understand this?

Next in Docker & Containers

Docker Compose

Continue