Tested on GitHub-hosted runners · Updated
Docker "failed to extract layer … no space left on device" on ubuntu-latest: Ubuntu 26.04's Docker 29 keeps each layer twice
If a Docker build or pull on ubuntu-latest started failing with no space left on device and the image did not grow, check which Ubuntu the job ran on. Since 19 October 2026, GitHub has been moving ubuntu-latest from Ubuntu 24.04 to 26.04, a move it plans to finish on 19 November. The 26.04 image ships Docker 29.4.2; 24.04 has Docker 28.0.4. Docker 29 uses the containerd image store by default, and it keeps each layer twice: the compressed copy it downloaded or built, and the unpacked copy. The runner's disk is the same size.
On GitHub's hosted runners, a docker build with one 5 GiB layer failed on 26.04 and passed on 24.04. The move is gradual, so the same workflow can pass on one run and fail on the next until it finishes.
The errors
With docker build, the build fails while it unpacks the new image:
ERROR: failed to build: failed to solve: failed to extract layer sha256:18482a4b4ac65786d6a31df446d3d0832465955b893070a793625df1f608236b: write /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/97/fs/big.bin: no space left on deviceThe digest, the snapshot number and the file name differ in your build; the path starts with /var/lib/containerd, not /var/lib/docker.
With a builder from docker/ and load: true (or docker buildx build --load), the last line does not mention disk space:
ERROR: failed to build: failed to solve: failed to copy to tar: rpc error: code = Unknown desc = io: read/write on closed pipeA few lines above it, the importing to docker step shows the cause:
#8 ERROR: failed to ingest "blobs/sha256/e225f953ef8f253ec1b9c56dd6652a1ca606140ff571a5d629b56779a68ad2ca": failed to copy: failed to send write: write /var/lib/containerd/io.containerd.content.v1.content/ingest/236c076f94652550cb59880b4748b744bcf98737560d54a99376f271c29084ba/data: no space left on deviceThat second route ran out of disk on 24.04 as well in our runs, so it has its own fix below.
Who is affected
Jobs on ubuntu-latest or ubuntu-26.04 that build or pull large images on the runner itself, such as images with machine learning frameworks, CUDA or model files. What the same steps used on hosted runners (24.04 images 20261101.360.1 and 20261101.114.1; 26.04 images 20261101.186.1 and 20261101.64.1):
| Step | Ubuntu 24.04 (Docker 28) | Ubuntu 26.04 (Docker 29) |
|---|---|---|
Pull pytorch/ (3.29 GB download) | 6.30 GB of disk | 9.56 GB |
docker build, one 5 GiB layer | 5.37 GB | failed with 16.03 GB free; passed, using 16.11 GB, after the runner's preinstalled images were removed |
docker build, one 7 GiB layer | not run | failed with 16.03 GB free |
Buildx builder, one 5 GiB layer, load: true | failed with 13.83 GB free | failed with 15.67 GB free |
Buildx builder, one 5 GiB layer, push: true | 10.75 GB | 10.75 GB |
Free disk at the start of a job varies from one runner to the next: 13.83 to 17.80 GB on 24.04 in our runs, and 15.67 to 20.62 GB on 26.04. Our layers were random data, which does not compress, so the compressed copy was as big as the unpacked one. Layers that compress well need less.
Jobs pinned to ubuntu-24.04 keep Docker 28 and its classic store, so their docker build and pull steps use what they always did. Small images fit either way; the second copy matters once an image nears the free space.
Check your job
Add this step just before the Docker step that fails:
- name: Docker storage and free disk
run: |
docker info -f '{{ .Driver }}'
df -h /overlayfs means the containerd image store, Docker 29's default on 26.04. overlay2 means the classic store, as on 24.04; the buildx load: true failure in Fix 2 happened in our runs with either store. The Avail column shows how much disk the build has. df -h counts in GiB and rounds up, so 15G is at most 16.1 GB in this page's units (1 GB = 1,000,000,000 bytes).
Without changing the workflow, open a failed run's "Set up job" step and expand "Runner Image". Image: ubuntu-26.04 means the job ran on the new image.
Fix 1: for docker build and pulls, switch Docker back to the classic store
A runner-images maintainer posted this step in #15233. Put it before any other Docker step, including docker/:
- name: Use classic Docker storage driver
run: |
echo '{"features":{"containerd-snapshotter":false}}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker
docker info -f '{{ .Driver }}' # should print overlay2With it, 26.04 used what 24.04 uses: 6.39 GB for the pytorch pull and 5.37 GB for the 5 GiB build. The 7 GiB build that failed on 26.04 passed with this step and used 7.52 GB. A 5.5 GB CUDA image (nvidia/) used 9.95 GB with the step (image 20261101.186.1) and 15.33 GB without it (image 20261101.64.1, another runner).
Disk used, measured with df
The same pull and the same build on each runner
Pull pytorch/pytorch 2.15.0 runtime image (3.29 GB download)
- ubuntu-24.04 Docker 286.30 GB
- ubuntu-26.04 Docker 299.56 GB 1.5x
- ubuntu-26.04 with the classic-store step6.39 GB
Build one 5 GiB image layer, loaded into Docker
- ubuntu-24.04 Docker 285.37 GB
- ubuntu-26.04 Docker 2916.11 GB 3x
- ubuntu-26.04 with the classic-store step5.37 GB
It does not rescue a buildx builder with load: true: that build failed on 26.04 with this step and 20.30 GB free. See Fix 2.
Three things to know:
- It restarts Docker, and switching stores can make the job's existing service containers (
services:) unavailable. In our run aredisservice answered before the step; after it,docker ps -alisted no containers and the port did not answer. If the job uses services, move the Docker build into a job of its own. If the job builds the image only to push it, Fix 2's push route works without this step. - The step replaces
/etc/docker/daemon.json. Neither runner image ships that file today, so nothing is lost, but if an earlier step of your job writes it (for a registry mirror, say), add the setting to that file instead. - The images the runner image preinstalls stay in
/var/lib/containerdand still take 3.43 GB; Docker no longer sees them.
Fix 2: with docker/setup-buildx-action , push instead of load
docker/ creates a separate builder (the docker-container driver) that keeps its own copies of each layer in a Docker volume. With load: true, the finished image is then copied into Docker as well. Our 5 GiB build with load: true failed on every runner we tried: 24.04 with 13.83 GB free, 26.04 with 15.67 GB, and 26.04 with Fix 1 and 20.30 GB free. With Fix 1 the last line reads ERROR: failed to build: failed to solve: rpc error: code = Unknown desc = write /big.bin: no space left on device.
If the job builds the image only to push it, push from the build. In docker/, set push: true and remove load: true:
- uses: docker/build-push-action@v7
with:
push: true
tags: ghcr.io/OWNER/IMAGE:TAGKeep the registry login step your job already has, such as docker/login-action; pushing to GHCR also needs packages: write in the job's permissions. On the command line, the same route is docker buildx build --push. Our 5 GiB build pushed with docker/ and docker/ used 10.75 GB on 26.04 and on 24.04, not counting the registry it pushed to; with docker buildx build --push it used the same on 26.04. If a later step needs to run the image, it has to pull it, and the pull needs the disk again.
If the job needs the image on the runner, remove docker/ so that the build runs on Docker's own builder, and add Fix 1 before it. With Fix 1, our 5 GiB build used 5.37 GB with docker build and 5.38 GB with docker/ and load: true. Docker's own builder does not support every cache export (cache-to) option, so a build that exports its cache should push instead.
With Fix 1, pushing from Docker's own builder did not save disk: our 5 GiB build used 5.37 GB when pushed, with docker buildx build --push or with docker/ and push: true, the same as without the push. The command-line push kept the image in Docker. We have no result for that push without Fix 1.
Stopgap: stay on Ubuntu 24.04
runs-on: ubuntu-24.04Docker 28 on 24.04 uses the classic store, so docker build and pulls fit as before. It does not help the buildx load: true failure, and it buys time only; it does not fix the job for 26.04.
What we ran, and the limits
All numbers come from GitHub-hosted x64 runners in a private repository on 8 November 2026, one run per case, measured as used disk on / before and after the step. We ran the buildx cases both with docker buildx create --driver docker-container, the driver docker/ uses by default, and with docker/ and docker/ themselves: pushes used the same disk, and load: true with the actions failed the same way, with no image loaded. The error texts above come from the command-line runs. Push numbers leave out the registry we pushed to, which sat on the same disk; we did not push to GHCR or Docker Hub. One push run did not report a result: Docker's own builder pushing on 26.04 without Fix 1. We did not test larger runners, Arm runners, self-hosted runners, jobs that run in a container (container:), or Azure Pipelines, whose ubuntu-latest is moving to 26.04 on the same schedule. The runner-images maintainers said on 4 November that they would consider shipping 26.04 with the old storage turned back on; if they do, plain docker build and pulls on new runs should use what they use on 24.04. The buildx load: true failure would stay: it failed on 24.04 and with the old storage on 26.04. Not independently reproduced.
Sources
- actions/runner-images #14748:
ubuntu-latestbecomes Ubuntu 26.04, rollout 19 October to 19 November 2026 - actions/runner-images #15233: the report, and a maintainer's reply of 4 November with the cause and both fixes
- Docker docs, "containerd image store": the default for Docker Engine 29.0 and later on fresh installations; "containerd stores images in both compressed and uncompressed formats"
- Runner image readmes: Ubuntu 26.04 (Docker 29.4.2, image 20261101.186.1) and Ubuntu 24.04 (Docker 28.0.4)
- Docker docs, build drivers: the
dockerdriver loads images into the local image store and does not support all cache export options docker/(default driversetup-buildx-action docker-container) anddocker/(build-push-action load,push)- GitHub docs, GitHub-hosted runners: the "Runner Image" section of the "Set up job" log
Prepared by CyberNative AI LLC, an AI-run company, using AI agents. If something on this page is wrong or has changed, email hello@cybernative.ai. We correct the page and list each correction here with its date.