Skip to content
BloGrove
devops

Docker Foundations: Images, Containers, and Layers Explained

What Docker actually does — images vs containers, the layer cache, the Dockerfile instructions that matter, and image-slimming that works.

BBloGrove Editorial3 min read
Docker Foundations: Images, Containers, and Layers Explained

Docker solves a problem every developer knows intimately: it works on my machine. Containers bundle an application with its entire runtime environment — dependencies, system libraries, configuration — into one portable unit that runs identically on laptops, CI, and production. This post builds the working mental model: what images and containers really are, how layers make everything fast, and how to write Dockerfiles that don't embarrass you.

The core distinction: images are blueprints, containers are instances#

  • Image: an immutable template — your app's files plus its runtime, frozen at a moment in time
  • Container: a running (or stopped) process created from an image

The analogy that sticks: images are classes, containers are objects. One image spawns unlimited containers; deleting a container never touches its image.

docker build -t myapp .          # image from Dockerfile
docker run -p 3000:3000 myapp    # container from image
docker ps                        # running containers

Containers feel like VMs but aren't. They share the host's kernel; isolation comes from kernel namespaces (separate views of processes, network, filesystem) and cgroups (resource limits). That's why containers start in milliseconds where VMs take minutes — there's no guest OS to boot, just a sandboxed process.

The layer cache: why builds get fast#

Images stack in layers — roughly one per Dockerfile instruction — and each layer is cached and shared:

FROM node:22-alpine
WORKDIR /app
COPY package*.json ./      # layer 3: only rebuilt when deps change
RUN npm ci                 # layer 4: cached unless package files change!
COPY . .                   # layer 5: changes constantly
RUN npm run build
CMD ["node", "dist/server.js"]

When any layer's input changes, it and everything after rebuilds — but everything before stays cached. This ordering is why professionals put dependency installation before source code copying: editing your code re-runs COPY . . but skips the expensive npm ci. Reverse those two steps and every code edit triggers full dependency reinstall — the single most common Dockerfile mistake.

The instructions that matter daily#

Instruction Does Gotcha
FROM base image pin versions (node:22, not latest)
COPY / ADD files in prefer COPY (ADD has magic)
RUN execute at build time chains with && share one layer
CMD default command at run time one per image, overridable
EXPOSE documentation of port doesn't publish anything
.dockerignore excludes build context first defense against fat builds

The RUN/CMD distinction trips everyone once: RUN happens when building the image (baked in forever); CMD is what executes when a container starts. Also add .dockerignore immediately (node_modules, .git, build artifacts) — without it, COPY . . ships gigabytes you already have elsewhere.

Slimming images: multi-stage builds#

Build tools and dev dependencies bloat images enormously. Multi-stage builds compile in one stage and copy only artifacts into a clean final stage:

FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]

Final image: no source, no compilers, no devDependencies — often 5–10x smaller. Smaller means faster deploys, smaller attack surface, less registry cost. Alpine bases shrink further (mind musl/glibc edge cases).

The workflow worth internalizing#

  1. Write minimal Dockerfile → 2. .dockerignore → 3. Order layers expensive-and-stable-first → 4. Multi-stage for anything compiled → 5. Run exactly what production runs locally

That last point is Docker's real payoff: your local container runs the same OS libraries, same Node version, same everything as deployment — eliminating an entire category of environment bugs. And it composes naturally into multi-service setups, where the same principles scale from one container to whole stacks.

Interview-ready summary: images = immutable layered templates, containers = isolated processes sharing host kernel, layer caching = rebuild only what changed, multi-stage = separate build-time from run-time. Four sentences that demonstrate you've actually used the tool.

Related: compose for multi-container apps · Kubernetes concepts · interview study plan

Enjoyed this article?

Share it with your network.

Share

Keep reading

Kubernetes Core Concepts, Explained for Developers
devops

Kubernetes Core Concepts, Explained for Developers

Kubernetes without the buzzwords — pods, deployments, services, ingress, config, and scaling, explained through the problems each solves.

4 min read
Anatomy of a Phishing Attack: How to Spot Them Every Time
security

Anatomy of a Phishing Attack: How to Spot Them Every Time

Phishing works through psychology, not technology — the emotional triggers, tell-tale signs in any message, and a verification routine that catches fakes.

3 min read