Marcio Cunha

Slow Docker build? How to use remote cache in CI

Learn how to speed up heavy image builds in continuous integration pipelines by configuring distributed caching layers with Docker Buildx.

Marcio Cunha•4 min
Also available in:EspañolPortuguês
Summary
  • Continuous integration pipelines without remote caching must download and rebuild dependencies from scratch on every run.
  • Docker Buildx and BuildKit allow exporting caching layers to a remote registry like Docker Hub or GitHub Cache.
  • The speed boost is dramatic because only modified Dockerfile instructions require reprocessing.
  • Storing distributed caches requires monitoring disk space on your provider and setting up expiration policies.
  • Proper integration with tools like Linux disk cleanup routines prevents storage exhaustion.

Why do Docker builds usually take so long in CI?

When we write code and push it to a repository, we expect the continuous integration pipeline—the automated system that tests and packages software—to run quickly. However, many teams face frustrating delays when packaging applications into Docker containers. In practice, this happens because each CI server typically starts the task with a completely clean virtual machine, having zero historical context of previous files.

This means that if you do not configure a mechanism to save intermediate progress, Docker will download all system libraries, reinstall language packages, and compile code from scratch on every single commit. This wasted time accumulates into hours lost throughout the week and delays important production deliveries. To better understand how container environments consume local resources and why constant cleanup is necessary, check out our insights on managing disk space and Docker cache.

The concept of layers and how BuildKit changes the game

To understand how to solve this problem, we first need to look inside a Docker image. Every command written in a Dockerfile—such as a RUN apt-get update or a COPY . .—generates an independent layer, acting like a temporary compressed archive. Docker is smart enough to reuse these layers locally if the corresponding file has not suffered any modifications.

The major bottleneck is that this intelligence used to remain locked inside the developer's local machine. When the build task ran on a disposable cloud server, all that layer history was discarded as soon as the process finished. This is where BuildKit comes in, the modern Docker build engine that replaced the legacy builder and brings native support for exporting and importing these cache layers externally.

How to configure remote caching using Buildx

Buildx is a command-line tool that expands Docker's capabilities, allowing multi-architecture builds and remote cache exports. In practice, we can configure our pipeline to save the cache directly to a container registry, such as Docker Hub or GitHub Packages, using inline or dedicated cache parameters.

To implement this in your pipeline, you need to initialize a custom builder and point to the export and import parameters. Here is a practical example of a command executed in your pipeline configuration file, such as GitHub Actions or GitLab CI:

docker buildx create --use --name my-builder
docker buildx build \
  --cache-from type=registry,ref=myuser/myimage:cache \
  --cache-to type=registry,ref=myuser/myimage:cache,mode=max \
  --tag myuser/myimage:latest \
  --push .

In this example, the --cache-from parameter tells Docker where to look for previously built layers before starting the process. Meanwhile, --cache-to ensures that upon a successful build, all newly generated layers are sent back to the remote registry, keeping the history up to date for the next developer or commit.

Choosing the right cache backend for your infrastructure

Although sending cache to a container registry is the most common approach, the BuildKit ecosystem offers other options that might better suit your company's infrastructure. You can choose to store cache in Amazon S3, on shared network directories, or by utilizing GitHub Actions' native caching service, which tends to be very fast since it resides on the same runner infrastructure.

Choosing the ideal backend involves a classic trade-off between transfer speed and operational complexity. If your image registry is hosted in the same geographical region as your CI servers, transferring cache layers over the network will be extremely fast. Otherwise, the time spent downloading gigabytes of remote cache might offset the intended speed benefit.

Essential maintenance and care for distributed caches

Using remote caching in CI brings impressive agility to the development workflow, but it also requires a maintenance plan to prevent uncontrolled storage consumption. Because the mode=max option instructs BuildKit to save absolutely all intermediate layers, the volume of data stored in your cache registry will continuously grow over the months.

To avoid unpleasant surprises on your cloud provider invoice or failures due to lack of space, configure retention policies and automatic cleanup in your container registry. Combining a robust caching strategy with good operational practices ensures your continuous integration environment stays fast, lean, and ready to deliver software with high frequency. To complement this implementation in production environments, see our related technical analyses on Pod network down? How to debug CNI in Kubernetes and Zombie processes on Linux? How to kill and find origin.

Conclusion

Optimizing Docker images in continuous integration environments is no longer a luxury, but a fundamental requirement for teams seeking fast and efficient delivery cycles. By adopting BuildKit alongside a well-configured remote cache backend, we eliminate idle recompilation time and free developers to focus on what truly matters: writing quality code. Evaluate your current infrastructure topology, choose the most appropriate cache storage, and start reaping the benefits of much faster builds today.