Cached Docker builds on GitHub Actions

September 2023

You can make Docker builds on GitHub Actions faster and cheaper by caching the layers in the Actions cache.

Check out the repo for a full example.

To set up caching, we need to:

  1. Enable the BuildKit engine.
  2. Expose the ACTIONS_CACHE_URL and ACTIONS_RUNTIME_TOKEN environment variables.
  3. Build with the GitHub Actions cache exporter.
name: "Build"
on:
  workflow_dispatch:
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      # 1)
      - name: Enable BuildKit
        uses: docker/setup-buildx-action@v2
 
      # 2)
      - name: Expose GitHub Runtime env vars
        uses: crazy-max/ghaction-github-runtime@v2
 
      # 3)
      - name: Build image using BuildKit and GHA cache exporter
        run: docker buildx build -t caching-test --load --cache-to type=gha,mode=max --cache-from type=gha .
      
      - name: Run test
        run: docker run --rm caching-test

Enable the BuildKit Engine

BuildKit is a modern Docker backend. It provides the GitHub Actions cache exporter.

Enable BuildKit by using the docker/setup-buildx-action@v2 action. This sets up a docker-container driver by default, which is what we need.

Instead of running docker build, use docker buildx build.

Expose the necessary environment variables

The cache exporter requires the ACTIONS_CACHE_URL and ACTIONS_RUNTIME_TOKEN environment variables to access the Actions cache. The quickest way is to use the crazy-max/ghaction-github-runtime@v2 action before the build step.

Build with the GitHub Actions cache exporter

Add --cache-to type=gha,mode=max --cache-from type=gha to your docker buildx build command to enable the Actions cache exporter. If you’re using the image outside of the BuildKit backend, you should also add --load so the image gets exported to the regular Docker context.

docker buildx build -t caching-test --load --cache-to type=gha,mode=max --cache-from type=gha .

mode determines which layers get cached. mode=min only caches layers that end up in our final image. mode=max caches intermediate layers as well. min is the default and smallest, max takes more space and increases the likelihood of cache hits.

Result

Once everything is hooked up, you’ll see caching mentioned in the logs and the Actions cache fill up with buildkit-blob-[…] files.

#5 [internal] load build context
#5 transferring context: 35.46kB 0.0s done
#5 DONE 0.0s

#8 [2/5] WORKDIR /usr/src/app
#8 CACHED

#9 [3/5] COPY requirements.txt ./
#9 CACHED

#10 [4/5] RUN pip install --no-cache-dir -r requirements.txt
#10 CACHED
#14 exporting to GitHub Actions Cache
#14 preparing build cache for export
#14 writing layer sha256:a8363e4c30df4ed75e94129f74e5b96f37814378b627b7df6071b97463c2b5b2
#14 writing layer sha256:a8363e4c30df4ed75e94129f74e5b96f37814378b627b7df6071b97463c2b5b2 0.3s done
#14 preparing build cache for export 1.4s done