Cache management with GitHub Actions

This page contains examples on using the cache storage backends with GitHub Actions.

Note

See Cache storage backends for more details about cache storage backends.

Inline cache

In most cases you want to use the inline cache exporter. However, note that the inline cache exporter only supports min cache mode. To use max cache mode, push the image and the cache separately using the registry cache exporter with the cache-to option, as shown in the registry cache example.

name: ci  on:   push:  jobs:   docker:     runs-on: ubuntu-latest     steps:       - name: Login to Docker Hub         uses: docker/login-action@v3         with:           username: ${{ vars.DOCKERHUB_USERNAME }}           password: ${{ secrets.DOCKERHUB_TOKEN }}        - name: Set up Docker Buildx         uses: docker/setup-buildx-action@v3        - name: Build and push         uses: docker/build-push-action@v6         with:           push: true           tags: user/app:latest           cache-from: type=registry,ref=user/app:latest           cache-to: type=inline

Registry cache

You can import/export cache from a cache manifest or (special) image configuration on the registry with the registry cache exporter.

name: ci  on:   push:  jobs:   docker:     runs-on: ubuntu-latest     steps:       - name: Login to Docker Hub         uses: docker/login-action@v3         with:           username: ${{ vars.DOCKERHUB_USERNAME }}           password: ${{ secrets.DOCKERHUB_TOKEN }}        - name: Set up Docker Buildx         uses: docker/setup-buildx-action@v3        - name: Build and push         uses: docker/build-push-action@v6         with:           push: true           tags: user/app:latest           cache-from: type=registry,ref=user/app:buildcache           cache-to: type=registry,ref=user/app:buildcache,mode=max

GitHub cache

Cache backend API

Availability: Experimental

The GitHub Actions cache exporter backend uses the GitHub Cache service API to fetch and upload cache blobs. That's why you should only use this cache backend in a GitHub Action workflow, as the url ($ACTIONS_RESULTS_URL) and token ($ACTIONS_RUNTIME_TOKEN) attributes only get populated in a workflow context.

name: ci  on:   push:  jobs:   docker:     runs-on: ubuntu-latest     steps:       - name: Login to Docker Hub         uses: docker/login-action@v3         with:           username: ${{ vars.DOCKERHUB_USERNAME }}           password: ${{ secrets.DOCKERHUB_TOKEN }}        - name: Set up Docker Buildx         uses: docker/setup-buildx-action@v3        - name: Build and push         uses: docker/build-push-action@v6         with:           push: true           tags: user/app:latest           cache-from: type=gha           cache-to: type=gha,mode=max
Important

Starting April 15th, 2025, only GitHub Cache service API v2 will be supported.

If you encounter the following error during your build:

ERROR: failed to solve: This legacy service is shutting down, effective April 15, 2025. Migrate to the new service ASAP. For more information: https://gh.io/gha-cache-sunset 

You're probably using outdated tools that only support the legacy GitHub Cache service API v1. Here are the minimum versions you need to upgrade to depending on your use case:

  • Docker Buildx >= v0.21.0
  • BuildKit >= v0.20.0
  • Docker Compose >= v2.33.1
  • Docker Engine >= v28.0.0 (if you're building using the Docker driver with containerd image store enabled)

If you're building using the docker/build-push-action or docker/bake-action actions on GitHub hosted runners, Docker Buildx and BuildKit are already up to date but on self-hosted runners, you may need to update them yourself. Alternatively, you can use the docker/setup-buildx-action action to install the latest version of Docker Buildx:

- name: Set up Docker Buildx   uses: docker/setup-buildx-action@v3   with:    version: latest

If you're building using Docker Compose, you can use the docker/setup-compose-action action:

- name: Set up Docker Compose   uses: docker/setup-compose-action@v1   with:    version: latest

If you're building using the Docker Engine with the containerd image store enabled, you can use the docker/setup-docker-action action:

-   name: Set up Docker   uses: docker/setup-docker-action@v4   with:     version: latest     daemon-config: |       {         "features": {           "containerd-snapshotter": true         }       }

Cache mounts

BuildKit doesn't preserve cache mounts in the GitHub Actions cache by default. To put your cache mounts into GitHub Actions cache and reuse it between builds, you can use a workaround provided by reproducible-containers/buildkit-cache-dance.

This GitHub Action creates temporary containers to extract and inject the cache mount data with your Docker build steps.

The following example shows how to use this workaround with a Go project.

Example Dockerfile in build/package/Dockerfile

FROM golang:1.21.1-alpine as base-build  WORKDIR /build RUN go env -w GOMODCACHE=/root/.cache/go-build  COPY go.mod go.sum ./ RUN --mount=type=cache,target=/root/.cache/go-build go mod download  COPY ./src ./ RUN --mount=type=cache,target=/root/.cache/go-build go build -o /bin/app /build/src ...

Example CI action

name: ci  on:   push:  jobs:   build:     runs-on: ubuntu-latest     steps:       - name: Login to Docker Hub         uses: docker/login-action@v3         with:           username: ${{ vars.DOCKERHUB_USERNAME }}           password: ${{ secrets.DOCKERHUB_TOKEN }}        - name: Set up QEMU         uses: docker/setup-qemu-action@v3        - name: Set up Docker Buildx         uses: docker/setup-buildx-action@v3        - name: Docker meta         id: meta         uses: docker/metadata-action@v5         with:           images: user/app           tags: |             type=ref,event=branch             type=ref,event=pr             type=semver,pattern={{version}}             type=semver,pattern={{major}}.{{minor}}        - name: Go Build Cache for Docker         uses: actions/cache@v4         with:           path: go-build-cache           key: ${{ runner.os }}-go-build-cache-${{ hashFiles('**/go.sum') }}        - name: Inject go-build-cache         uses: reproducible-containers/buildkit-cache-dance@4b2444fec0c0fb9dbf175a96c094720a692ef810 # v2.1.4         with:           cache-source: go-build-cache        - name: Build and push         uses: docker/build-push-action@v6         with:           cache-from: type=gha           cache-to: type=gha,mode=max           file: build/package/Dockerfile           push: ${{ github.event_name != 'pull_request' }}           tags: ${{ steps.meta.outputs.tags }}           labels: ${{ steps.meta.outputs.labels }}           platforms: linux/amd64,linux/arm64

For more information about this workaround, refer to the GitHub repository.

Local cache

Warning

At the moment, old cache entries aren't deleted, so the cache size keeps growing. The following example uses the Move cache step as a workaround (see moby/buildkit#1896 for more info).

You can also leverage GitHub cache using the actions/cache and local cache exporter with this action:

name: ci  on:   push:  jobs:   docker:     runs-on: ubuntu-latest     steps:       - name: Login to Docker Hub         uses: docker/login-action@v3         with:           username: ${{ vars.DOCKERHUB_USERNAME }}           password: ${{ secrets.DOCKERHUB_TOKEN }}        - name: Set up Docker Buildx         uses: docker/setup-buildx-action@v3        - name: Cache Docker layers         uses: actions/cache@v4         with:           path: ${{ runner.temp }}/.buildx-cache           key: ${{ runner.os }}-buildx-${{ github.sha }}           restore-keys: |             ${{ runner.os }}-buildx-        - name: Build and push         uses: docker/build-push-action@v6         with:           push: true           tags: user/app:latest           cache-from: type=local,src=${{ runner.temp }}/.buildx-cache           cache-to: type=local,dest=${{ runner.temp }}/.buildx-cache-new,mode=max        - # Temp fix         # https://github.com/docker/build-push-action/issues/252         # https://github.com/moby/buildkit/issues/1896         name: Move cache         run: |           rm -rf ${{ runner.temp }}/.buildx-cache           mv ${{ runner.temp }}/.buildx-cache-new ${{ runner.temp }}/.buildx-cache