Cache management with GitHub Actions
This page contains examples on using the cache storage backends with GitHub Actions.
NoteSee 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=inlineRegistry 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=maxGitHub cache
Cache backend API
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=maxImportantStarting 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-sunsetYou'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-actionordocker/bake-actionactions 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 thedocker/setup-buildx-actionaction to install the latest version of Docker Buildx:- name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 with: version: latestIf you're building using Docker Compose, you can use the
docker/setup-compose-actionaction:- name: Set up Docker Compose uses: docker/setup-compose-action@v1 with: version: latestIf you're building using the Docker Engine with the containerd image store enabled, you can use the
docker/setup-docker-actionaction:- 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/arm64For more information about this workaround, refer to the GitHub repository.
Local cache
WarningAt the moment, old cache entries aren't deleted, so the cache size keeps growing. The following example uses the
Move cachestep as a workaround (seemoby/buildkit#1896for 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