As a Docker power user, you likely know that image caching can speed up builds substantially by reusing layers from previous builds. However, sometimes Starting from a clean slate is necessary. The --no-cache
flag forces Docker to build without referencing any cache, ensuring every layer gets rebuilt from scratch.
In this comprehensive guide, we’ll cover:
- How image caching works in Docker and when it’s useful
- Scenarios when rebuilding without cache is recommended
- Using
--no-cache
properly withdocker-compose build
- Additional cache-related options like
--pull
- Best practices for leveraging caching for faster builds
So let’s dive in!
A Primer on Docker Image Caching
Before jumping into --no-cache
, understanding what caching does behind the scenes will be useful.
As you may know, Docker images are made up of read-only layers that represent filesystem changes and instructions like adding files or installing packages. These layers get cached locally and reused in subsequent builds if the exact same files and commands are used again.
This means that only new layers need to be built, constructed, and stored when handling incremental changes in a Dockerfile. By skipping steps for unchanged layers, caching can drastically speed up image build and rebuild times.
Here’s a visual representation of layers caching in action on two incremental Dockerfile builds:
Based on benchmarks, build performance gains from utilizing cache effectively can be upwards of 15-20x faster versus building from scratch! This makes caching an incredibly useful optimization.
However, there are also scenarios when rebuilding without cache is useful.
When to Disable Caching with --no-cache
You get immense speed gains from caching in Docker, so why ever disable it?
Here are some common reasons you may want to rebuild an image completely from scratch without cache:
Updating and testing dependencies – Having old packages and libraries in your image cache can cause issues down the line if they become outdated, vulnerable, or incompatible. Rebuilding without cache ensures everything gets updated.
Reproducing builds – Disable caching to guarantee reproducibility by ruling out variability from cache contents. Forces using only what’s defined in your Dockerfile.
Diagnosing errors – If suspecting cache problems are causing unexpected errors or bugs, try --no-cache
to verify a clean build works properly.
Additional context on dependencies – Seeing full outputs installs steps rather than cached ones can provide more insight into how dependencies are configured.
Testing full rebuild times – Useful for optimizing Dockerfiles to have efficient cache-less rebuild times as well for worst case scenarios.
The --no-cache=true
option gives us a tool disable image caching when we want to guarantee a fully fresh build.
Using --no-cache
with docker-compose build
While you can pass --no-cache
to docker build
, using it with docker-compose
facilitates rebuilding multi-service applications cleanly.
Here is an example workflow for rebuilding a basic Node.js web application with no caching via compose:
1. Define services in docker-compose.yml
version: "3.8"
services:
web:
build:
context: .
dockerfile: Dockerfile
image: web:latest
ports:
- "8080:8080"
db:
image: mysql:5.7
volumes:
- dbdata:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: password
volumes:
dbdata:
2. Build images using cache by default
$ docker-compose build
Building web
[+] Building 0.3s (7/7) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 190B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 50B 0.0s
=> [internal] load metadata for docker.io/library/node:16-alpine 0.0s
=> CACHED [1/3] FROM docker.io/library/node:16-alpine 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 1.42kB 0.0s
=> [2/3] RUN npm install 0.3s
=> [3/3] COPY . . 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:2d6d02a2a6df979bf51705934360deea15ab8b60ec80548c05e38c2596c48a15 0.0s
=> => naming to docker.io/library/web:latest
3. Add –no-cache to do a clean rebuild
$ docker-compose build --no-cache
Building web
[+] Building 0.8s (12/12) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 190B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 50B 0.0s
=> [internal] load metadata for docker.io/library/node:16-alpine 0.0s
=> [ 1/12] FROM docker.io/library/node:16-alpine 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 1.42kB 0.0s
=> CACHED [ 2/12] WORKDIR /app 0.0s
=> [ 3/12] COPY package*.json ./ 0.0s
=> [ 4/12] RUN npm install 0.4s
=> [ 5/12] COPY . . 0.0s
=> [ 6/12] EXPOSE 8080 0.0s
=> [ 7/12] USER node 0.0s
=> [ 8/12] ARG PORT=8080 0.0s
=> [ 9/12] ENV PORT 8080 0.0s
=> [10/12] ADD https://github.com/ufoscout/docker-compose-wait/releases/do 0.0s
=> [11/12] RUN chmod +x /wait 0.0s
=> [12/12] CMD /wait && node server.js 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.1s
=> => writing image sha256:790e6f69b75fc658b12e8fcf0d168193aa0ef4efa034ee4907cf385dd655c4f1 0.0s
=> => naming to docker.io/library/web:latest 0.0s
Now Docker will fully rebuild the web image without using any cached layers at all.
We can also drop the --build
flag entirely on subsequent runs to avoid side effects from persistent build cache:
$ docker-compose up --no-build
Alternatives Like --pull
for Updated Dependencies
The --no-cache
option does a complete clean rebuild as we‘ve seen. But sometimes you only care about refreshed dependencies rather than the entire image.
This is where --pull
comes in handy.
--pull
works similarly by ignoring the local image cache, but has a more limited scope – it will only re-pull the base image specified in FROM
to get updated packages. Existing custom layers will still get cached.
Here is how this looks in practice:
So in summary:
--no-cache
= Rebuild entire image from scratch--pull
= Re-download base image only
Choose the option that best suits your use case!
Best Practices for Leveraging Caching Effectively
Now that you understand how disabling cache works, let’s shift gears and talk best practices to leverage caching properly.
Here are some tips for optimizing Dockerfiles and composing applications to maximize cache hits:
- Order layers from least to most change – Put installer layers first before adding app code to avoid reinstalls.
- Separate installs and source code – Isolate dependency steps like
npm install
to different layers thanCOPY
app code. - Use .dockerignore – Exclude files not needed for builds like logs, tmp files, and documentation from the context.
- Don’t install unnecessary packages – Streamline your base images and only install essential dependencies.
- Use multi-stage builds – Isolate dependencies to be cached separately from final production images.
- Pin versions – Lock down base image, package, and dependency versions for consistent caching.
Following these best practices will ensure you squeeze the most performance out of caching capabilities.
Conclusion
Adding a --no-cache
option to docker-compose build
facilitates completely rebuilding Docker applications from scratch when necessary. This disables cache to refresh dependencies, validate reproducibility, diagnose issues, and more.
But caching can also speed up Docker builds tremendously when leveraged properly. Be sure to also implement cache optimization best practices to take full advantage of performance gains from incremental caching in day-to-day development.
Hopefully this guide gave you a comprehensive understanding of how image caching works under the hood and when disabling it with --no-cache
makes sense. Happy (faster) building!