Docker Fundamentals
Image vs container. Layers, COW, Dockerfiles.
Docker Fundamentals
A container is an isolated process running on top of a shared host kernel. An image is the frozen template those processes are launched from. The two words get used interchangeably in conversation, but they are very different objects on disk.
Analogy
Think of a bakery's frozen cookie dough. The sealed tube on the shelf is the image — inert, identical to every other tube off the line, and it can sit there forever. The warm cookies on the tray are containers — baked from a tube, eaten, gone. You can pull a dozen tubes off the shelf, bake them all at once, throw the crumbs away, and the tubes are still unchanged. That is why the same image can spawn a thousand identical processes on a thousand machines.
Image vs container
An image is a stack of read-only layers, each a tarball of filesystem changes, addressed by content hash.
A container is an image plus one writable layer on top. When you docker run nginx, Docker takes the nginx image, adds a thin scratch layer, and starts a process in it. Stop the container and the writable layer can be discarded — the image underneath is untouched.
docker images # what's frozen on disk
docker ps # what's currently running
docker ps -a # including stopped containers
Layers and copy-on-write
Every layer-producing line in a Dockerfile (FROM, RUN, COPY, ADD, WORKDIR) adds one layer. Metadata lines (ENV, EXPOSE, CMD, ENTRYPOINT, USER, LABEL) change only the image config; they do not add filesystem layers.
When a container writes to a file that lives in a read-only layer, the storage driver copies the file up into the writable layer and modifies the copy. The original stays untouched. This is copy-on-write, and it's how ten containers from the same image can share 99% of their bytes on disk.
A minimal Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["node", "server.js"]
Five layers get written (FROM, WORKDIR, COPY, RUN, COPY), one metadata entry (CMD).
Order matters: each line's cache key is a hash of the previous state and the line itself. Change line 4 and lines 1–3 still hit the cache; everything from line 4 down is rebuilt. That's why dependency installs live before application source.
Multi-stage builds
A single Dockerfile can define multiple stages, each starting with its own FROM. Later stages can copy files from earlier stages; everything else is discarded.
FROM golang:1.22 AS build
WORKDIR /src
COPY . .
RUN go build -o /app/server .
FROM gcr.io/distroless/static
COPY --from=build /app/server /server
ENTRYPOINT ["/server"]
The final image contains only the binary and the distroless base — no Go toolchain, no /bin/sh, no apt. That is both smaller and meaningfully safer — there's nothing in there for an attacker to work with.
Tags, digests, and pulling
docker pull alpine:3.19 # by tag (mutable)
docker pull alpine@sha256:… # by digest (immutable)
docker run --rm -it alpine:3.19 sh
Tags are labels that the registry is free to repoint; digests are content-addressable and never change. Production pipelines should resolve tags to digests on build and ship the digest.
Inspecting images
docker image inspect alpine:3.19 # JSON: layers, env, entrypoint
docker history alpine:3.19 # layer-by-layer breakdown
docker run --rm -it alpine:3.19 sh # exec a shell inside
docker history is particularly useful — it shows how each layer was created and how big it is, so you can see where the 400 MB of bloat came from.
Images are not VMs
A container does not boot a kernel, does not run init, does not have its own /boot. It is a tree of processes that the host kernel has placed into Linux namespaces (which is what makes ps show only the container's own processes) and cgroups (which is what enforces CPU and memory limits). Level 2 takes that apart.
Tools in the wild
6 tools- cliDockerfree tier
The reference container engine + CLI; `build`, `run`, `compose` are the daily verbs.
- libraryBuildKitfree tier
Modern Docker builder with cache mounts, secrets, and concurrent layer builds.
- cliPodmanfree tier
Daemonless, rootless Docker drop-in — friendly to security-conscious environments.
- cliDivefree tier
TUI for inspecting image layers; pinpoints exactly which COPY blew up your image size.
- cliTrivyfree tier
Aqua's vulnerability scanner for images, IaC, and SBOMs — runs in CI in seconds.
- cliDocker Composefree tier
Multi-container dev environments from a single `docker-compose.yml` file.