🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

What is a Container Image Layer Explained Simply

Container image layers are the building blocks of Docker images. Learn how layers work, why they matter for build speed and security, and how to keep your images lean.

DevOpsBoysJun 7, 20264 min read
Share:Tweet

A Docker image isn't a single file. It's a stack of read-only layers, each representing one instruction in the Dockerfile. Understanding layers is what separates engineers who have 200MB images from those who have 2GB images.


What is a Layer?

Each RUN, COPY, and ADD instruction in a Dockerfile creates a new layer. A layer is a diff — it records only what changed from the previous layer.

dockerfile
FROM ubuntu:22.04          # Layer 1: base OS (77MB)
RUN apt-get update         # Layer 2: updated package index
RUN apt-get install -y nginx  # Layer 3: nginx + dependencies
COPY ./config /etc/nginx/  # Layer 4: your config files
CMD ["nginx", "-g", "daemon off;"]  # metadata only, no new layer

The final image is: Layer 1 + Layer 2 + Layer 3 + Layer 4 stacked on top of each other.


Visualizing Layers

bash
docker history nginx:latest
IMAGE          CREATED       CREATED BY                            SIZE
a99a39d070bf   2 weeks ago   CMD ["nginx" "-g" "daemon off;"]     0B
<missing>      2 weeks ago   STASH /etc/nginx/conf.d/default.conf  1.09kB
<missing>      2 weeks ago   RUN /bin/sh -c apt-get install nginx  58.3MB
<missing>      2 weeks ago   RUN /bin/sh -c apt-get update         18.1MB
<missing>      2 weeks ago   ADD file:ubuntu-22.04.tar.gz          77.8MB

Each line = one layer. SIZE shows how much that layer adds to the image.


Why Layers Matter: Caching

Docker caches every layer. If a layer hasn't changed, it reuses the cached version instead of re-running the instruction. This is what makes rebuilds fast.

CACHED [1/4] FROM ubuntu:22.04
CACHED [2/4] RUN apt-get update
CACHED [3/4] RUN apt-get install nginx
Step 4/4: COPY ./config /etc/nginx/    ← changed, runs again

Cache invalidation rule: Once any layer changes, Docker re-runs that layer AND every layer after it.

This is why order matters in Dockerfiles:

dockerfile
# BAD — changing any source file invalidates the npm install cache
FROM node:20
COPY . .                 # copy everything first
RUN npm install          # re-runs whenever ANY file changes
 
# GOOD — npm install only re-runs when package.json changes
FROM node:20
COPY package.json package-lock.json ./
RUN npm install          # cached until package.json changes
COPY . .                 # source code copied after install

How Layers Are Stored

Docker stores layers as compressed tar archives on disk:

bash
# See layer storage on your system
ls /var/lib/docker/overlay2/
 
# Each directory = one layer
# "overlay2" = the storage driver (uses copy-on-write)

Layers are content-addressed — identified by a SHA256 hash of their contents. This means:

  • Two images sharing the same base layer share the same bytes on disk
  • Pulling ubuntu:22.04 twice downloads it once

Shared Layers Across Images

Image A:
  [ubuntu:22.04] → [nginx] → [config-A]

Image B:
  [ubuntu:22.04] → [nginx] → [config-B]

Both images share the ubuntu:22.04 and nginx layers. On disk, those bytes exist once. Only config-A and config-B are separate.

This is also why Docker pull shows "already exists" for most layers when pulling an updated image — only the changed layers download.


The Writable Container Layer

When you run a container, Docker adds one writable layer on top of the read-only image layers:

[Read-Only Image Layers]  ← shared across all containers
       ↓
[Writable Container Layer] ← unique per container, deleted when container stops

Writing to files inside a running container goes to this writable layer (copy-on-write). The original image layers never change.

This is why containers start instantly — no copying of the image, just a new writable layer pointer.


Layer Anti-Patterns (and Fixes)

Secrets in layers

dockerfile
# BAD — API key is baked into layer history forever
RUN export API_KEY=secret123 && curl -H "Authorization: $API_KEY" ...
 
# GOOD — use BuildKit secrets
RUN --mount=type=secret,id=api_key \
    API_KEY=$(cat /run/secrets/api_key) && curl -H "Authorization: $API_KEY" ...

Even if you unset the variable or delete the file in a later layer, docker history can still expose values from earlier layers.

Unnecessary layer bloat

dockerfile
# BAD — 3 RUN commands = 3 layers, cache files stick around
RUN apt-get update
RUN apt-get install -y curl wget git
RUN rm -rf /var/lib/apt/lists/*  # this delete is in a NEW layer, doesn't shrink image
 
# GOOD — single layer, cleanup in same RUN
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl wget git && \
    rm -rf /var/lib/apt/lists/*

Multi-Stage Builds: Discarding Heavy Layers

The best way to reduce final image size is multi-stage builds — you build in one image, copy only the output to a minimal final image:

dockerfile
# Build stage — has all the tools (large)
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN go build -o server .
 
# Final stage — only the binary (tiny)
FROM scratch
COPY --from=builder /app/server /server
CMD ["/server"]

The Go toolchain, source code, and intermediate files never appear in the final image. Only the compiled binary.


Quick Layer Inspection

bash
# Dive tool — visual layer explorer
docker run --rm -it \
  -v /var/run/docker.sock:/var/run/docker.sock \
  wagoodman/dive myimage:latest
 
# Check image size breakdown
docker images --format "{{.Repository}}:{{.Tag}}\t{{.Size}}" | sort -k2 -h

Dive is the best tool for understanding exactly what each layer adds — it shows which files each layer creates, modifies, or deletes, and calculates the wasted space.

Layers are simple in concept but matter enormously for image size, build time, and security. Always ask: does this instruction create a layer that needs to exist in the final image?

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments