DEV Community

Cover image for Docker Layer Caching Explained: Tips to Improve Build Times
Dhananjay Patel
Dhananjay Patel

Posted on • Originally published at blog.thenanjay.com

Docker Layer Caching Explained: Tips to Improve Build Times

Introduction

In our previous discussion on Docker image optimization, we focused on reducing image size to achieve faster deployments and lower storage costs. Now, let’s address another vital aspect of Docker workflows: build speed.

The time it takes to build a Docker image can significantly impact your development and deployment cycles. Fortunately, Docker offers a powerful feature called layer caching that can drastically reduce build times by reusing unchanged layers from previous builds.

In this blog, we’ll dive into how Docker layer caching works, practical tips to use it effectively, and common pitfalls to avoid.


What Is Docker Layer Caching?

Docker images are constructed layer by layer, with each instruction in the Dockerfile creating a new layer.

For example:

FROM node:16  
WORKDIR /app  
COPY package.json .  
RUN npm install  
COPY . .  
CMD ["npm", "start"]  
Enter fullscreen mode Exit fullscreen mode

In this Dockerfile, each instruction (FROM, WORKDIR, COPY, etc.) generates a new layer in the image. Docker saves these layers in the cache. If a subsequent build encounters an instruction that hasn’t changed, Docker reuses the cached layer instead of recreating it, speeding up the build process.


Why Is Layer Caching Important?

  • Faster Builds: Reusing cached layers reduces the time spent on unchanged instructions.

  • Improved Development Workflow: Iterative changes become quicker to test and deploy.

  • Cost Efficiency: Shorter build times reduce compute resource usage in CI/CD pipelines.


How Docker Layer Caching Works

Docker processes the Dockerfile sequentially:

  1. It examines the first instruction.
  2. If the instruction hasn’t changed since the last build, Docker uses the cached layer.
  3. Once a layer’s cache is invalidated, all subsequent layers are rebuilt.

For instance:

  • If COPY package.json changes, the cache for the RUN npm install step will also be invalidated. (same example as above)

  • Instructions after the invalidated layer will not benefit from caching.


Best Practices for Leveraging Docker Layer Caching

  1. Organize Your Instructions Thoughtfully

To maximize caching, place instructions that rarely change at the top of your Dockerfile.

Example:

# Better
COPY package.json package-lock.json .  
RUN npm install  
COPY . .  
Enter fullscreen mode Exit fullscreen mode

In this example, updates to your application code (copied in the last step) won’t invalidate the cached npm install layer.

  1. Leverage Multi-Stage Builds

Multi-stage builds allow you to separate the build and runtime environments, reducing unnecessary layers in the final image.

Example for Node.js App:

# Build Stage
FROM node:16 AS builder  
WORKDIR /app  
COPY package.json package-lock.json ./  
RUN npm install  
COPY . .  
RUN npm run build  

# Runtime Stage
FROM nginx:alpine  
COPY --from=builder /app/build /usr/share/nginx/html  
CMD ["nginx", "-g", "daemon off;"]  
Enter fullscreen mode Exit fullscreen mode

This approach ensures that only the production-ready artifacts are included in the final image, significantly reducing size and build time.

  1. Use .dockerignore to Avoid Irrelevant Files

Include a .dockerignore file to exclude unnecessary files like .git directories, logs, or node_modules that could invalidate caching.

Example .dockerignore:

node_modules  
*.log  
.git 
Enter fullscreen mode Exit fullscreen mode
  1. Avoid Frequent Changes to Dependency Files

Modifications to files like package.json or requirements.txt can invalidate cache for subsequent layers. If possible, group and minimize such changes.

  1. Combine Commands to Reduce Layers

Each instruction creates a new layer. Combining commands into a single RUN statement minimizes layer count and keeps images compact.

RUN apt-get update && apt-get install -y curl vim \  
    && apt-get clean && rm -rf /var/lib/apt/lists/*  
Enter fullscreen mode Exit fullscreen mode

or there is one more option, Rather than endless && \ statements this would be more readable, especially for more complex runs.

RUN <<EOF
apt-get update
apt-get install -y curl vim 
apt-get clean
rm -rf /var/lib/apt/lists/*  
EOF
Enter fullscreen mode Exit fullscreen mode
  1. Bump Cache for Layer-Sensitive Changes

When changes in dependencies (e.g., bumping package.json version) invalidate a cache, consider temporary techniques like pre-defining dependency versions to isolate changes.


Common Pitfalls to Avoid

  1. Changing Order of Instructions

Rearranging Dockerfile instructions can invalidate the cache for no reason. Be consistent in the order.

  1. Neglecting Cleanup

Temporary files in one layer persist unless explicitly removed in the same instruction.

Fix:

RUN apt-get update && apt-get install -y curl \  
    && rm -rf /var/lib/apt/lists/*  
Enter fullscreen mode Exit fullscreen mode
RUN <<EOF
apt-get update
apt-get install -y curl vim 
apt-get clean
rm -rf /var/lib/apt/lists/*  
EOF
Enter fullscreen mode Exit fullscreen mode
  1. Forgetting the Build Context

Large files in the build context can slow down COPY or ADD instructions and invalidate the cache.


Tools to Enhance Build Speed

  1. BuildKit

Docker BuildKit offers advanced caching mechanisms and parallelism for faster builds.

In the another blog I will deep dive into Buildkit.

as of now just Enable BuildKit:

DOCKER_BUILDKIT=1 docker build .  
Enter fullscreen mode Exit fullscreen mode
  1. Layer Caching in CI/CD

Many CI/CD platforms, like GitHub Actions and GitLab CI, support Docker layer caching to avoid rebuilding unchanged layers in every pipeline run.


Conclusion

Docker layer caching is a game-changer for accelerating builds and optimizing your workflows. By structuring your Dockerfile smartly, leveraging tools like BuildKit, and avoiding common pitfalls, you can drastically reduce build times and improve developer productivity.

Start experimenting with these techniques, and let me know how much faster your builds become! 🚀

Top comments (2)

Collapse
 
vikram_palled_d9adc55ac6f profile image
VIKRAM PALLED

Great insight on both aspects of docker. Please keep posting them.

Collapse
 
thenanjay profile image
Dhananjay Patel

Thank you for the kind words! 😊 I'm glad you found the posts helpful. I'll definitely keep sharing more Docker insights—stay tuned!