Effective BuildKit cache in GitHub Actions

Hidetake Iwata
2 min readOct 3, 2021

--

Docker BuildKit supports cache. It would reduce time to build an image, especially for multistage Dockerfile.

This article explains efficient cache strategy in pull request based development flow.

Problem to solve

You can import and export cache by passing the following config to docker/build-push-action:

- uses: docker/build-push-action@v2
with:
cache-from: type=registry,ref=IMAGE
cache-to: type=registry,ref=IMAGE,mode=max

It always imports the cache from registry and exports the cache to registry.

In pull request based development flow, cache is overwritten by pull requests and it may cause cache miss. For example,

  1. Initially main branch is built and cache is set to A
  2. When pull request B is opened, cache is hit and overwritten to B
  3. When pull request C is opened, cache is missed and overwritten to C
  4. When pull request B is merged into main branch, cache is missed and overwritten to D

Solution

We can improve cache efficiency by the following design:

  • Cache always points to the base branch
  • Don’t export cache on a pull request

This diagram shows relationship of commit and cache.

pull_request event

When a pull request is opened, it need to import the cache of base branch. Do not export the cache to prevent cache pollution. For example,

cache-from: type=registry,ref=IMAGE:main
cache-to:

push event of branch

When a branch is pushed, it need to import and export the cache of pushed branch. For example,

cache-from: type=registry,ref=IMAGE:main
cache-to: type=registry,ref=IMAGE:main,mode=max

Other events

Otherwise, it need to import the cache of triggered branch. Do not export the cache to prevent cache pollution. For example,

cache-from: type=registry,ref=IMAGE:main
cache-to:

How to use

I wrote int128/docker-build-cache-config-action to generate a cache config. You can use it with docker/build-push-action as follows:

      - uses: docker/metadata-action@v3
id: metadata
with:
images: ghcr.io/${{ github.repository }}
- uses: int128/docker-build-cache-config-action@v1
id: cache
with:
image: ghcr.io/${{ github.repository }}/cache
- uses: docker/build-push-action@v2
id: build
with:
push: true
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
cache-from: ${{ steps.cache.outputs.cache-from }}
cache-to: ${{ steps.cache.outputs.cache-to }}

It sets the cache config as follows:

  • On pull request, only cache-from is set to the base branch
  • On push of branch, both cache-from and cache-to are set
  • Otherwise, only cache-from is set to the triggered branch

Conclusion

This article explains efficient cache strategy in pull request based development flow.

You can use int128/docker-build-cache-config-action to generate a config for docker/build-push-action.

Enjoy DevOps!

--

--