Skip to main content
Course/Primitives/Container Images β€” Build, Tag & Load
8/845 min
Primitives

Container Images β€” Build, Tag & Load

Build container images with Dockerfiles, use multi-stage builds, and load images into minikube for local development.

beginner45 minRead β†’ Lab β†’ Quiz β†’ Practice
🧠 Brain Warm-Up
🧠 Brain Warm-Up: Every container you have run in this course came from a container image. Where do those images come from, and what is actually inside them? Before reading, write down what you think a Dockerfile does and how layers work.

What Is a Container Image?

A container image is a read-only, layered filesystem bundled with metadata (entrypoint, environment variables, labels). When the container runtime starts a container, it adds a thin writable layer on top β€” this is the union filesystem (overlay2 on Linux).

Each image is built from a Dockerfile β€” a sequential recipe of instructions. Each instruction that modifies the filesystem produces a new immutable layer. Layers are cached and shared across images.

Core Dockerfile Instructions

InstructionPurpose
FROMBase image to start from
RUNExecute a shell command during build (adds a layer)
COPYCopy files from host into the image
ADDLike COPY but also handles URLs and tar extraction
WORKDIRSet working directory for subsequent instructions
ENVSet environment variable (persists into containers)
EXPOSEDocument which port the app listens on (informational only)
CMDDefault command when container starts (overridable at runtime)
ENTRYPOINTFixed command β€” CMD becomes its arguments

CMD vs ENTRYPOINT

# CMD only β€” overridable entirely
CMD ["nginx", "-g", "daemon off;"]

# ENTRYPOINT + CMD β€” ENTRYPOINT fixed, CMD provides default args
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]

If you run docker run myimage -c /etc/nginx/nginx.conf, ENTRYPOINT+CMD form passes -c /etc/nginx/nginx.conf as args to nginx. CMD-only form replaces the entire command with -c /etc/nginx/nginx.conf β€” often not what you want.

Layer Caching

Docker caches each layer. If an instruction and its inputs haven't changed, Docker reuses the cached layer and skips re-running it. Instruction order matters: put frequently changing instructions (e.g., COPY your source code) late; put stable instructions (e.g., package installs) early.

# Good: apt install cached unless requirements.txt changes
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .          # only this layer invalidated on code changes

# Bad: every code change busts the apt cache
COPY . .
RUN pip install -r requirements.txt

Multi-Stage Builds

A multi-stage Dockerfile uses multiple FROM instructions. Earlier stages compile or build artifacts; the final stage copies only what's needed at runtime. Build tools (gcc, maven, node_modules) stay in the builder stage and never make it into the final image.

# Stage 1: builder
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o server .

# Stage 2: runtime (tiny)
FROM alpine:3.19
COPY --from=builder /app/server /server
CMD ["/server"]

Result: a Go binary in a 10 MB Alpine image instead of a 700 MB Go SDK image.

Using Local Images in minikube

minikube runs its own Docker daemon isolated from your host. To use a locally built image:

# Option A: build inside minikube's docker daemon
eval $(minikube docker-env)
docker build -t myapp:v1 .

# Option B: build on host, then load into minikube
docker build -t myapp:v1 .
minikube image load myapp:v1

Then set imagePullPolicy: Never in your Pod spec β€” otherwise Kubernetes tries to pull from the internet and fails with ImagePullBackOff.