How can I share a Docker layer cache across GitHub Actions jobs?

Send every Docker build to the same Depot project. Depot automatically persists the BuildKit layer cache on remote NVMe storage so GitHub Actions jobs, workflow runs, and teammates can reuse it without uploading cache archives.

Last updated 2026-09-09

Use Depot container builds and send every Docker build to the same Depot project. Depot automatically persists the BuildKit layer cache on remote NVMe storage. Every authenticated build using that project can reuse the cache, whether it comes from another GitHub Actions job, a later workflow run, or a teammate's local machine.

There are no cache archives to upload. No cache-from or cache-to configuration. No dependency on the filesystem of a particular runner.

The Depot project ID is the cache boundary. Jobs using the same project share cache. Jobs using different projects don't.

Share one Depot project across jobs

This workflow builds two services as separate GitHub Actions matrix jobs. Both builds use the same Depot project, so BuildKit can reuse common base-image, system-package, and dependency layers:

name: Build containers

on:
  pull_request:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-24.04
    permissions:
      contents: read
      id-token: write
    strategy:
      matrix:
        include:
          - service: api
            context: services/api
          - service: worker
            context: services/worker
    steps:
      - uses: actions/checkout@v4
      - uses: depot/setup-action@v1

      - name: Build ${{ matrix.service }}
        uses: depot/build-push-action@v1
        with:
          project: ${{ vars.DEPOT_PROJECT_ID }}
          context: ${{ matrix.context }}

Configure a GitHub OIDC trust relationship in the Depot project, then store its ID in a repository or organization variable named DEPOT_PROJECT_ID. The id-token: write permission lets each job authenticate without a static Depot token.

The matrix jobs are separate runner machines. They still reach the same remote build service and project cache namespace. Future builds of either service can reuse any valid layers already present in that project.

If one job must populate cache before another job starts, make the consumer job depend on the producer with needs. That ordering is only necessary when the second job must use layers created earlier in the same workflow. It isn't required to reuse cache from previous builds.

Runner-local Docker state isn't shared

GitHub-hosted runners are ephemeral. A local docker build writes layers into the Docker daemon on that runner. When the job finishes, the runner and its Docker state disappear.

Another job gets another runner. Even two jobs in the same workflow can't see each other's local Docker daemon. Choosing the same runs-on label doesn't make the filesystem or /var/lib/docker persistent.

This leaves two ways to reuse the cache:

  1. Export the cache from the first runner to remote storage, then download it into the next runner.
  2. Run the build remotely where the cache already persists.

Depot uses the second model. The GitHub Actions runner sends the build context to a remote BuildKit builder. The builder uses the project's existing cache and writes new layers back to it automatically. The runner can disappear without taking the cache with it.

GitHub cache still moves the cache over the network

The common BuildKit configuration on ephemeral runners uses cache-from: type=gha and cache-to: type=gha,mode=max. That exports build cache to the GitHub Actions cache service at the end of one build and imports it at the start of another.

It works, but it turns the cache into data that must be serialized, uploaded, indexed, downloaded, and unpacked. Large Docker caches can spend enough time moving over the network to erase much of the time saved by a cache hit. Cache limits and eviction also mean a warm build can unexpectedly become cold.

With Depot, the cache stays on persistent NVMe storage next to the remote builders. A new job connects to the cache where it already lives. When using depot/build-push-action, remove cache-from: type=gha and cache-to: type=gha. Depot caching is automatic, and exporting the same cache to GitHub is unnecessary.

Cache scope follows the Depot project

Depot doesn't create a separate Docker layer cache for every GitHub Actions job or branch. The project is the cache namespace.

That means:

  • Jobs in different workflows share cache when they use the same Depot project.
  • Builds on trusted branches can reuse valid layers produced on other trusted branches.
  • Developers using depot build locally share cache with CI when they use the same project.
  • Different repositories can intentionally share cache by using the same project.
  • Separate Depot projects provide separate cache namespaces when isolation matters more than reuse.

BuildKit still decides whether a layer is valid. Sharing a project doesn't bypass normal Docker cache invalidation. A changed COPY, RUN, build argument, base image, or Dockerfile instruction can force that layer and everything after it to rebuild.

For open-source repositories, Depot isolates builds launched from fork pull requests. Those builds run without read or write access to the main project cache, preventing untrusted code from reading or poisoning it.

A shared cache isn't a shared image

The layer cache accelerates another build. It doesn't put the finished image into the Docker daemon of every GitHub Actions runner.

If a later job needs the exact image produced by an earlier job, save it to Depot Registry, push it to another container registry, or explicitly transfer it as an artifact. If the later job only needs to rebuild the same Dockerfile quickly, point it at the same Depot project and let BuildKit reuse the cached layers.

For most GitHub Actions workflows, the practical setup is simple: one Depot project per application or closely related set of images, the same project ID in every build job, and no GitHub cache exporter. That shares the Docker layer cache across CI jobs, workflow runs, and local development without turning the cache into another artifact to move around.

Start building with Depot
in minutes