How Container Images Work

2 minute read

How Container Images Work

A container image is built in layers. Each instruction in a Dockerfile creates a new layer. Layers are cached, so rebuilding is fast when only a few layers change.

┌─────────────────────────┐
│   Application Code      │  ← Your app layer
├─────────────────────────┤
│   Dependencies (npm,    │  ← Libraries layer
│   pip, go modules)      │
├─────────────────────────┤
│   Runtime (Node, Python │  ← Runtime layer
│   Go binary)            │
├─────────────────────────┤
│   Base OS (alpine,      │  ← Base image layer
│   debian, scratch)      │
└─────────────────────────┘

Image Layers in Detail

Every FROM, RUN, COPY, and ADD instruction creates a new layer. Other instructions (ENV, EXPOSE, CMD) only add metadata and don’t create filesystem layers.

Layer 4:  COPY . .                    (app code — changes often)
Layer 3:  RUN pip install -r req.txt  (dependencies — changes sometimes)
Layer 2:  COPY requirements.txt .     (dep file — changes sometimes)
Layer 1:  FROM python:3.12-slim       (base image — rarely changes)

Key insight: Docker uses a layer cache. If a layer hasn’t changed, Docker reuses it from cache instead of rebuilding. This is why you copy dependency files (like package.json or requirements.txt) before copying your full application code — the dependency layer stays cached unless dependencies change.

Image Naming Convention

registry/repository:tag
Part Description Example
Registry Where the image is stored docker.io, gcr.io, registry.example.com
Repository Image name (optionally with namespace) library/nginx, team/my-app
Tag Version identifier 1.27, latest, sha-abc123

Examples:

  • nginx:1.27 — Docker Hub, nginx repo, tag 1.27
  • docker.io/library/nginx:latest — fully qualified name
  • gcr.io/my-project/api:v2.1 — Google Container Registry
  • my-registry.com/team/app:sha-abc123 — custom registry with commit SHA

Exam tip: Always use specific tags (like nginx:1.27) instead of latest. Using latest makes deployments unpredictable.

Image Digests

A digest is a SHA256 hash of the image manifest, providing an immutable reference:

nginx@sha256:abc123def456...

Unlike tags (which can be moved to point to different images), digests always reference the exact same image content.

How Images Are Stored and Distributed

Developer                  Registry                    Kubernetes Node
┌──────────┐   push    ┌──────────────┐   pull    ┌──────────────┐
│ docker    │ ────────→ │ Docker Hub / │ ────────→ │ containerd / │
│ build     │           │ ECR / GCR    │           │ CRI-O        │
└──────────┘           └──────────────┘           └──────────────┘
  1. Build — The image is created locally from a Dockerfile
  2. Push — The image is uploaded to a registry
  3. Pull — Kubernetes (via the container runtime) downloads the image to the node
  4. Run — The container runtime creates a container from the image

Image Size Matters

Smaller images are better for several reasons:

Benefit Why
Faster pulls Less data to download from the registry
Faster deployments Pods start quicker, especially on cold nodes
Less storage Saves disk space on nodes
Smaller attack surface Fewer packages = fewer vulnerabilities

Common base image sizes:

Base Image Size
ubuntu:22.04 ~77 MB
debian:bookworm-slim ~74 MB
alpine:3.19 ~7 MB
gcr.io/distroless/static ~2 MB
scratch 0 MB

Updated: