What's actually in an image
An image is metadata plus a list of layers. Each layer is a diff of the filesystem produced by one instruction in a Dockerfile — mostly RUN, COPY, and ADD. Layers are identified by a content hash, stored once, and shared between any images that happen to produce the same layer.
When you run a container from an image, Docker doesn't touch the image's layers at all. It adds one more layer on top — a thin writable layer unique to that container — using a union filesystem (overlay2, on modern Linux). Delete the container and that writable layer goes with it; the image underneath is untouched and ready to start another container.
Pulling and inspecting an image
# Pull an image from a registry (Docker Hub by default)
docker pull node:22-alpine
# List images you have locally, with their size
docker images
# See every layer in an image and how big each one is
docker history node:22-alpine
# Full metadata: entrypoint, exposed ports, env, layer digests
docker inspect node:22-alpineWhy layer order matters for build speed
Docker caches each layer and reuses it on the next build if the instruction and everything above it are unchanged. Put the things that change least often first, so a code change doesn't invalidate your dependency install.
FROM node:22-alpine
WORKDIR /app
# Dependencies change far less often than source code, so install them
# in their own layer, before copying the rest of the app in.
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
# Now copy the source. Editing app code only invalidates the layers
# from here down -- the npm ci layer above stays cached.
COPY . .
CMD ["node", "server.js"]Flip those two COPY steps and every single source change forces a full npm ci on every build.
Tags vs. digests
A tag like node:22-alpine is a mutable pointer — the maintainers can (and do) push a new image under the same tag when they patch it. For a reproducible build, pin to the immutable digest instead:
docker pull node:22-alpine
docker inspect --format='{{index .RepoDigests 0}}' node:22-alpine
# node@sha256:3b2c1f...
# Use that digest anywhere you need an exact, unchanging image
docker pull node@sha256:3b2c1f...A digest only proves the content hasn't changed -- it doesn't say who published it. For that, see signing & verification.
Building and tagging
# Build the Dockerfile in the current directory, tag it twice
docker build -t docktionary/app:1.4.0 -t docktionary/app:latest .
# Push both tags to a registry
docker push docktionary/app:1.4.0
docker push docktionary/app:latest
# Remove dangling (untagged) images left over from rebuilds
docker image prune