DevOpsLesson
DevOpsLesson

Free, comprehensive DevOps tutorials and learning roadmaps. Master Docker, Kubernetes, CI/CD, and more.

Stay Updated

Get notified about new tutorials and features.

Tutorials

  • What is DevOps?
  • Docker Tutorial
  • Terraform Tutorial
  • CI/CD Pipeline
  • All Tutorials

Roadmaps

  • DevOps Engineer
  • Cloud Engineer
  • SRE Path
  • All Roadmaps

Company

  • About Us
  • Blog
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 DevOpsLesson. All rights reserved.

DOCKERKUBERNETESTERRAFORMAWSCI/CDLINUXGITDEVOPS ROADMAPCLOUD ROADMAPSRE ROADMAPGIT CHEATSHEETDOCKER CHEATSHEETK8S CHEATSHEETTF CHEATSHEETLINUX CHEATSHEETDOCKERFILE LINTERYAML VALIDATORCRON PARSERREGEX TESTER

Docker Tutorial

Introduction to docker
Why Use Docker?
Docker vs Virtual Machines
Installing Docker
Key Docker Concepts
Docker Images
Docker Containers
Writing Dockerfiles
Docker Volumes

Layers & Caching

PreviousPrev
Next

Understand how Docker's layer system works and how to use build caching to make your builds dramatically faster.

What Are Docker Image Layers?

A Docker image isn't a single big file. It's a stack of thin, read-only snapshots called layers. There is one layer attached to for each instruction in your Dockerfile that makes a change to the filesystem.

Here's a simple Node.js Dockerfile with each layer labeled:

FROM node:18-alpine      # Layer 1: base OS + Node runtime (~127MB)
WORKDIR /app             # Layer 2: metadata only, takes up 0 bytes
COPY package*.json ./    # Layer 3: adds package.json (~2KB)
RUN npm install          # Layer 4: adds node_modules (~40MB)
COPY . .                 # Layer 5: adds your source code (~100KB)
CMD ["node", "app.js"]   # Layer 6: metadata only, takes up 0 bytes

Visually, the final image looks like this:

Docker Image Layers (top = newest)

CMD ["node", "app.js"]← metadata, no size
COPY . .← your app code
RUN npm install← node_modules
COPY package*.json .← package.json
WORKDIR /app← metadata, no size
FROM node:18-alpine← base image
Writable Container Layer← added at runtime

Image layers are read-only and shared between containers

Each layer only stores the difference from the layer below it, similar to how Git commits work. This means if two different images use node:18-alpine as their base, that base layer is stored only once on your disk and shared between them.

When you run a container, Docker adds one extra thin writable layer on top of the stack. The image layers underneath stay read-only and untouched, which is why multiple containers can run from the same image without interfering with each other.


How Docker's Build Cache Works

After Docker builds a layer, it caches it. The next time you build, Docker checks each instruction from top to bottom. If nothing has changed in that instruction or the files it touches, Docker skips re-executing it and uses the cached version instead. This is what makes rebuilds so much faster:

First build (nothing cached):
FROM node:18-alpine     ✓ downloaded fresh
WORKDIR /app            ✓ created
COPY package*.json ./   ✓ copied
RUN npm install         ✓ installed, let's say it took 30 seconds
COPY . .                ✓ copied
→ Total: ~35 seconds

Second build (only your app code changed):
FROM node:18-alpine     ✓ CACHED (skipped)
WORKDIR /app            ✓ CACHED (skipped)
COPY package*.json ./   ✓ CACHED (skipped)
RUN npm install         ✓ CACHED (skipped), saves 30 seconds
COPY . .                ✗ REBUILT (source code changed)
→ Total: ~2 seconds

There's one critical rule about how cache invalidation works: when a layer is invalidated, every layer that comes after it is also rebuilt, even if those later instructions didn't change. This is the key insight that shapes how you should order your Dockerfile.


Order of Dockerfile Matters

Put instructions that change rarely near the top. Put instructions that change often near the bottom.

The most common mistake beginners make is this:

The wrong way, rebuilds dependencies every single time

FROM node:18-alpine
WORKDIR /app
COPY . .               # Copies everything, including your source code
RUN npm install        # Invalidated every time ANY file changes
CMD ["node", "app.js"]

The problem here: COPY . . copies your source code along with package.json. Every time you change even one line of code, that COPY layer is invalidated, and since RUN npm install comes after it, npm has to reinstall everything from scratch in every single build.

The right way, reuses the dependency cache unless packages change

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./  # Copy ONLY the dependency manifest first
RUN npm install        # This only reruns when package.json changes
COPY . .               # Copy your source code after dependencies are installed
CMD ["node", "app.js"]

Now npm install is cached as long as package.json and package-lock.json stay the same. Changing your app code only rebuilds the last COPY layer. A 35-second build becomes a 2-second build.

Note: This isn't a Node.js-specific trick - it applies everywhere. The pattern is always the same: copy the dependency file first, install, then copy your source code.


Hands-On Practice: Watch the Cache in Action

The best way to understand caching is to see it yourself:

# Step 1: Build the image for the first time (everything runs fresh)
docker build -t cachetest:v1 .

# Step 2: Build again without changing anything
docker build -t cachetest:v1 .

# Step 3: Make a small change to your app code, then build again
docker build -t cachetest:v1 .

On the second build, you'll see Using cache next to every step. On the third build after a code change, you'll see Using cache for the dependency steps but REBUILT for the source code step. That's the cache doing exactly what it should.


When You Need to Skip the Cache

Sometimes you want Docker to ignore the cache, for example, to pick up the latest security patches from apt even though the instruction string hasn't changed:

# Force a completely fresh build (no cached layers)
docker build --no-cache -t myapp:latest .

If you only want to bust the cache from a specific point forward (without rebuilding everything), you can use a build argument as a cache-busting trick:

ARG CACHE_BUST=1
RUN apt-get update && apt-get upgrade -y
# Pass a new value to force this layer and everything after it to rebuild
docker build --build-arg CACHE_BUST=$(date +%s) -t myapp:latest .

Cleaning Up the Build Cache

The build cache can quietly grow to gigabytes over time. Here's how to manage it:

# See how much space the cache is using
docker system df

# Remove build cache that's no longer needed
docker builder prune

# Remove all build cache, including layers still referenced by images
docker builder prune -a

docker system df is a good habit to run occasionally. It gives you a quick overview of how much space images, containers, volumes, and build cache are each taking up.


Key Takeaways

  • Docker images are stacks of read-only layers, and each Dockerfile instruction that changes the filesystem creates a new one.
  • Layers are cached after the first build. If nothing changed, Docker reuses the cached layer and skips re-executing it.
  • When a layer is invalidated, all layers below it are rebuilt too, this is the most important caching rule to internalize.
  • Always copy your dependency manifest (package.json, requirements.txt, etc.) and run your install command before copying your source code. This one change can turn a 30-second build into a 2-second build.
  • Use docker build --no-cache when you need a guaranteed fresh build, such as picking up security patches.
  • Run docker builder prune periodically to reclaim disk space from accumulated build cache.

Frequently Asked Questions

Why does Docker rebuild everything after I change one file?

Because you're probably copying all your files (including source code) before installing dependencies. When COPY . . comes before RUN npm install, any file change invalidates the install layer. Fix it by copying your dependency manifest first, installing, then copying the rest of your code.

How does Docker know if a layer has changed?

For COPY and ADD instructions, Docker compares the actual file contents, not just timestamps. For RUN instructions, Docker compares the instruction string itself. If the string is identical, Docker assumes the result is the same and uses the cache.

Does the cache work across different machines?

Not by default. The local build cache lives only on your machine. In CI/CD pipelines you can use cache backends (like storing layers in a registry) to share cache across machines, but that's an advanced topic.

Why do some layers show 0B in docker history?

Those are metadata-only instructions like WORKDIR, ENV, EXPOSE, and CMD. They don't add or modify any files in the image, so they take up no space, but they still create a layer.

When should I use --no-cache?

Use it when you need to pull in external updates that Docker can't detect, most commonly when running apt-get update or pip install to get the latest security patches. Since the instruction string hasn't changed, Docker would normally use the cache and miss the new patches.

PreviousPrev
Next

Continue Learning

Docker Pull & Search

Learn how to pull Docker images from Docker Hub and search for the right ones using the CLI.

5 min·Easy

List & Inspect Images

Learn how to view, filter, and inspect Docker images on your local system to understand their contents and metadata.

6 min·Easy

Building Images

Learn to write Dockerfiles and use docker build to create custom images tailored to your applications.

10 min·Easy

Explore Related Topics

Ku

Kubernetes Tutorials

Orchestrate your Docker containers at scale

CI

CI/CD Tutorials

Automate Docker builds and deployments in pipelines

Try the Tool

Dockerfile Linter

Instantly lint your Dockerfile for best-practice violations and security issues.

On This Page

What Are Docker Image Layers?How Docker's Build Cache WorksOrder of Dockerfile MattersThe wrong way, rebuilds dependencies every single timeThe right way, reuses the dependency cache unless packages changeHands-On Practice: Watch the Cache in ActionWhen You Need to Skip the CacheCleaning Up the Build CacheKey TakeawaysFrequently Asked Questions