Fix GitHub Actions Buildx Cache Reservation Errors
Summary
Pixel’s announcement explains why GitHub Actions may report “failed to reserve cache” during the final Buildx cache export, even after an image is built and pushed. It distinguishes read-only cache access on events like pull requests from race conditions in matrix jobs, helping the community target the right fix.
DEV Community
error writing layer blob: failed to reserve cache
"error writing layer blob: failed to reserve cache" in GitHub Actions is the buildx gha cache exporter being refused when it asks the Actions cache service to reserve an entry. The image was usually built and pushed already; what failed is the cache export, and the fix is to stop exporting from the jobs that either may not write or are racing each other for the same key. The export is the last thing a build does, which is why the failure arrives after the image is already pushed. What this error means The build log looks like a success until the last few seconds. The image is built, the push completes, and then the step fails on the export with "ERROR: failed to solve: error writing layer blob: failed to reserve cache". Sometimes one blob is written before it stops, which is what makes it look like a quota problem. The three things people check first come back clean: the cache is nowhere near its limit, the permissions are right, and the same job passed yesterday. It is intermittent on a matrix and consistent on a fork pull request, and those two shapes have different explanations. The block below is quoted from the canonical issue rather than from a run of ours, and the section on reproduction says why. ```Quoted from docker/build-push-action#1571 ERROR: failed to solve: error writing layer blob: failed to reserve cache
Common causes
The workflow has read-only cache access
The most common cause and the most deterministic. Events such as pull_request_target and issue_comment get read-only access by default, so a build under one can read a cache and cannot reserve an entry. It fails identically every run, which is how you tell it from the matrix race.
You raised the limits and added permissions, and it kept failing
The wasted fix, and the reason the canonical issue runs as long as it does. Its reporter confirmed the cache was well inside the limit, added actions: write, tested two major versions of the action and checked the toolchain, and none of it changed the outcome. Quota and permissions are the first two guesses and usually not the answer.
Two matrix legs reserve the same key at the same time
Layer blobs are keyed by digest, ref and repository, and legs that share a base image share those digests. When two exports collide the second reservation is refused. It is intermittent by nature, and adding scope= does not separate them because the scope is not part of the reservation key.
The toolchain predates the cache service v2 cutover
Since April 2025 only the v2 cache API is served, and Buildx, BuildKit, Compose and Engine each have a minimum version that speaks it. A pinned action or a self-hosted image that has not moved will fail on the export even though everything about the workflow is correct.
How to fix it
Import everywhere, export from one place
- Remove
cache-tofrom pull request and comment-triggered workflows, and leavecache-fromin place. - Populate the cache from the push workflow on your default branch, where write access exists.
- Verify by checking that the cache entries appear after a merge rather than after a pull request build.
```.github/workflows/ci.yml
pull_request: read the cache, never write it
- uses: docker/build-push-action@v7
with:
cache-from: type=gha
push to the default branch: write it
- uses: docker/build-push-action@v7
with:
cache-from: type=gha
cache-to: type=gha,mode=max
Stop matrix legs from exporting the same layers Export from one leg rather than all of them, or give the legs genuinely different base layers. Exporting from a single leg keeps the benefit, because the layers a matrix shares are exactly the layers the other legs will import next time. - uses: docker/build-push-action@v7
with:
cache-from: type=gha
cache-to: ${{ matrix.node == 22 && 'type=gha,mode=max' || '' }}
Bring the toolchain up to the v2 minimums Update setup-buildx-action and the build action to current majors, and on a self-...

Powered by Readybot.io