How to Speed Up Your Dockerfile with BuildKit Cache Mounts

If you have a long (and ever-changing) list of apt-get packages in your Dockerfile, this one is for you.

BuildKit, a new build engine shipped with Docker, introduced a build-time cache mounts feature, which can be used to avoid long download times during image rebuilds.

By using cache mounts in your Dockerfile, you can skip re-downloading your complete package list and only fetch what’s missing. This can make a huge difference when it comes to the build times of your Docker images.

Let’s walk through a small example of a cache mount-using Dockerfile and see how it works!

The Building Blocks

Let’s go through the most important Dockerfile lines first. Don’t worry we’ll put them together to a complete example right afterwards.

“Alright BuildKit, We’re Using Your Features”

This new first line of your Dockerfiles tells BuildKit that you’re using BuildKit features:

# syntax = docker/dockerfile:1.2

“Hey APT, Don’t Delete The Downloaded Packages”

If you want to cache packages on the OS-level, you might need to do some tuning. For example, with Ubuntu the Docker image contains configurations to delete cached files after a successful install. Using a cache mount would not make sense with this configuration in place, as the files would be deleted during the install step. You can remove that configuration file like this:

RUN rm -f /etc/apt/apt.conf.d/docker-clean

If you want to cache other parts, you’ll need to create caching directories & configure your tooling to use them. To apply this to pip for example, you’d need to set an environment variable to point pip to the right caching directory. If you use other tools, you might need to configure them in a different fashion.

“BuildKit, Mount The Cache Please”

Last but not least, you’ll need to put a --mount flag between the RUN part of your Dockerfile’s instructions and the commands which they run. It looks like this:

RUN --mount=type=cache,target=/var/cache/apt ...

If you want to mount multiple different directories for a single instruction, you’d specify multiple --mount flags after each other.

A Complete, Minimal Dockerfile Example

Taking all of the building blocks above, here is what a minimal Dockerfile which uses BuildKit cache mounts looks like:

# syntax = docker/dockerfile:1.2
FROM ubuntu:18.04
ENV PIP_CACHE_DIR=/var/cache/buildkit/pip
RUN rm -f /etc/apt/apt.conf.d/docker-clean
RUN --mount=type=cache,target=/var/cache/apt \
	apt-get update &&
	apt-get install -yqq --no-install-recommends \
$YOUR_PACKAGES && rm -rf /var/lib/apt/lists/*

As metioned above, we set an environment variable to configure pip, and make sure the directory exists. However, we don’t use either in that snippet.

The $YOUR_PACKAGES variable we use in the installation command is just a placeholder, you’d need to replace it by the packages you want to install.

If you’re wondering about the --no-install-recommends flag, that’s there to tell apt-get to only install what’s strictly necessary instead of “nice-to-have” packages which are probably not needed. Removing the contents of the /var/lib/apt/lists directory usually saves around 30 megabytes from the final image. Those details are not needed to make BuildKit cache mounts work, but they are useful to know about.

In Conclusion

I hope this article will help you to start using BuildKit cache mounts and speed up your Docker image re-builds. If you haven’t already, you might want to learn about the other cool BuildKit features besides cache mounts - they’re really neat.

If you want to read more about BuildKit usage details, check out this documentation page on GitHub.