Container Images β Build, Tag & Load
Build container images with Dockerfiles, use multi-stage builds, and load images into minikube for local development.
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
| Instruction | Purpose |
|---|---|
FROM | Base image to start from |
RUN | Execute a shell command during build (adds a layer) |
COPY | Copy files from host into the image |
ADD | Like COPY but also handles URLs and tar extraction |
WORKDIR | Set working directory for subsequent instructions |
ENV | Set environment variable (persists into containers) |
EXPOSE | Document which port the app listens on (informational only) |
CMD | Default command when container starts (overridable at runtime) |
ENTRYPOINT | Fixed 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.