One of the keys to building a Docker image quickly is making use of the layer cache as frequently as possible. More cache hits means faster build times. Here we show how you can harness the power of Docker's layer cache when working in GitHub Actions.
What is the Docker layer cache?
The Docker layer cache is responsible for caching a series of Docker layers, stacked on top of each other. A Docker layer is the output of running a step in your Dockerfile (like RUN
, ADD
, COPY
, and so on).
When building a Docker image, you typically start with a base image, and then run a series of steps defined in your Dockerfile that add customization to the base image.
Dockerfiles ensure that your build steps are run in a particular order. Each step outputs a layer, which is used as an input to the next step. Every time Docker runs a build, it reuses the unchanged layers, but if one layer has changed, then that layer and all subsequent layers that depend on it need to be rebuilt. Layers at the top of a Dockerfile will often remain unchanged, whereas layers at the bottom will change more often.
In the example below, a file in the source working directory has changed, meaning that the COPY . .
command has produced a slightly different output layer. As this layer has changed, it can no longer be reused — and neither can any subsequent layers.
The benefit of Docker layer caching is it allows you to avoid wasting time rebuilding layers that are exactly the same as those from your previous build, which will make your overall Docker image build faster.
For a deeper dive into how the different cache layers interact with each other, see our Fast Dockerfiles: theory and practice post.
How to use Docker layer caching in GitHub Actions
By default, GitHub Actions doesn't persist the Docker layer cache across builds, because each Docker build is executed on a new, ephemeral GitHub Actions runner. Consequently, each build starts with a fresh, empty cache.
By making some simple changes to your GitHub Actions workflow, it's possible to get around this issue and persist the cache between builds. The main change required is to export the layer cache off the ephemeral runner. In this guide, we'll explore two ways to do this: using the GitHub Actions cache exporter and using Depot.
Using the GitHub Actions cache exporter
The GitHub Actions cache exporter is an experimental cache exporter for the GitHub Actions cache provided by buildx and BuildKit. The exporter orchestrates the storage of layer cache directly via the GitHub Cache API. When a new build is initiated, the cache can be restored via the GitHub Cache API so that your subsequent build can reuse previous build results in the layer cache.
To set up Docker layer caching using the GitHub Actions cache exporter, you'll need to set up a standard GitHub Actions workflow that builds a Docker image, and then add the GitHub Actions cache exporter to this.
First, create a GitHub Actions workflow file called ci.yml
inside the .github/workflows
directory at the root of your repository. Below, we show a typical workflow file for building a Docker image, highlighting the changes you'll need to make to add the GitHub Actions cache exporter to your workflow.
Adding the GitHub Actions cache exporter is easy: update the build-push-action
step in your workflow by adding cache-from
and cache-to
arguments, and set the cache type to gha
. This will export the cache to object storage via the GitHub Cache API.
If you commit your new ci.yml
file, you should see a Docker build completed via GitHub Actions.
By adding the cache-from
and cache-to
lines for remote cache saving and loading, you can reuse your Docker layers from previous builds — as we see with the CACHED
hits below.
Although the GitHub Actions cache exporter solves the problem of being able to reuse the cache across builds, it introduces some new problems:
Network transfer time negates any performance benefits: Loading and saving the cache is network bound, meaning it often negates any performance benefits of using the cached layers, particularly for simple image builds.
10 GB cache size limit: The GitHub Actions exporter relies on the GitHub Actions Cache API, which only supports a cache size of up to 10 GB for your entire repository. This is a problem for more complex images that need a larger cache.
The cache can't be shared: The cache is locked to GitHub Actions and can't be used in other systems or on local machines.
A faster alternative: using Depot with GitHub Actions
We built Depot to eliminate the limitations above, not only in GitHub Actions but in all CI providers.
Depot is a build acceleration platform that started with making Docker image builds up to 40x faster. We do it by managing a fleet of optimized remote BuildKit builders that support both x86 and ARM architecture natively without the need for emulation during your Docker image build. Each builder comes with 16 CPUs, 32 GB of memory, and a persistent NVMe cache disk that is up to 500 GB.
We persist your Docker layer cache directly to fast NVMe storage backed by Ceph. So you never have to think about saving or loading your layer cache over networks; it's just immediately available across builds.
Another benefit of Depot is that, unlike the GitHub Actions cache exporter, Depot's cache isn't tied to GitHub Actions. This means it can be shared with other CI/CD systems or used on your local machine.
How to use Depot for GitHub Actions Docker layer caching
-
You need to configure a project in Depot before getting started. A project is a cache namespace that provides a way to isolate separate Docker layer caches.
-
Switch out
docker/setup-buildx-action
anddocker/build-push-action
for Depot's own versions:depot/setup-action
anddepot/build-push-action
. -
Set up permissions to allow Depot to build your projects. If you're using OIDC authentication, set the
permissions
block as it's shown below. If not, you need to pass your Depot project token in thewith
block of your configuration file.
Below, we show a typical GitHub Actions YAML workflow configuration file with the above steps added to it:
For more examples of how to configure Depot in your GitHub Actions workflow for different types of builds like building multi-platform images or pushing to ECR, see our GitHub Actions integration guide.
Faster GitHub Actions caching with Depot
Depot's combination of using a faster cache and running builds on bigger and faster machines results in up to 40x faster Docker builds.
In addition, if you use Depot to speed up your Docker builds in GitHub Actions, you don't need to think about saving and loading the cache, as we persist it for you across builds automatically. The cached layers are available instantly during builds, with no need to save or load cached layers from the network, saving lots of time in CI builds.
The Docker layer cache is even shared with anyone who has access to the project, so a developer who runs a build locally can just reuse the cached layers that CI computed.
If you're interested in trying out Depot in your GitHub Actions workflow, you can try it out now with our 7-day free trial.
Now also available: Depot-hosted GitHub Actions runners. 30% faster compute than GitHub Actions' default runners, with 10x the cache performance, at half the cost.