Clear the Docker build cache, and what each command cannot reach
The command to clear docker build cache is docker builder prune, and the reason people run it twice is that it reaches only the builder it is pointed at: on a runner we measured, it reclaimed 180.7 MB from the default builder and exactly 0 B from the docker-container builder sitting beside it with 399.6 MB. On a GitHub Actions runner the whole question is usually moot, because a fresh job starts with an empty cache.

The build cache is not inside your images, so deleting images frees none of it, and docker system df will happily report tens of gigabytes under a row that docker image prune never touches. It has its own commands, and there is more than one of them because there is more than one place the cache can be.
This page is ordered the way the problem actually presents: first where the cache is, then what each command reclaimed when we ran it, then the two commands people expect to clear something that do not.
On a hosted runner there is usually nothing to clear
A GitHub-hosted or otherwise ephemeral runner gives every job a new machine. We read the store at the top of a job before anything had been built, and every row was zero: no images, no containers, no volumes, and a build cache of 0 B in 0 entries. There is nothing to prune because there is nothing there.
If you are on an ephemeral runner and reading this page because builds are slow, the problem is the opposite of the one the page title describes. Your builds start cold every time, and the fix is a cache backend that outlives the runner rather than a command that removes one. If you are on a self-hosted runner that is reused between jobs, read on, because that machine does accumulate and it will eventually fill its disk.
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 0 0 0B 0B
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0BRead it before you clear it
Two commands, and you want both. docker system df gives you the Build Cache row beside images, containers and volumes, so you can see whether the cache is even your problem. docker buildx du gives you the same store per builder, which is the reading that matters once more than one builder exists.
On our runner, one build of a three-stage Node image put 15 entries and 180.7 MB into the default builder, of which 109.3 MB was immediately reclaimable and the rest was held because it was still referenced by the image that build produced.
docker system df
docker buildx du
docker buildx du --builder mybuilder # per builder, once you have more than oneWhat each prune actually reclaimed
The two forms are not a style choice. Without -a, prune removes only the dangling entries, the ones nothing references any more, and that was 109.3 MB of the 180.7 on our runner. With -a it takes the rest as well, which was the remaining 71.4 MB, and left the store at 0 B.
Both were fast: 0.424 seconds and 0.134. The reason the first run so often disappoints on a developer machine is exactly this split. On a box that rebuilds one project all day, most of the cache is still referenced by the latest image, so the default form frees the smaller half and you conclude the command does not work.
| Command | Reclaimed | Left behind | Took |
|---|---|---|---|
docker builder prune -f | 109.3 MB | 71.4 MB | 0.424 s |
docker builder prune -a -f | 71.4 MB | 0 B | 0.134 s |
docker buildx prune --builder bl -a -f | 399.6 MB, all of builder bl | 0 B | 0.656 s |
docker buildx rm bl | the whole builder | nothing of it | 0.199 s |
The cache docker builder prune cannot see
This is the finding worth the page. docker/setup-buildx-action creates a builder on the docker-container driver, and that builder keeps its cache in a volume of its own rather than in the daemon store. We built once on such a builder and then read both: docker system df reported a Build Cache of 0 B, while docker buildx du for that builder reported 399.6 MB, sitting in a 349.4 MB local volume.
Then we ran docker builder prune -a -f and it reported a total of 0 B reclaimed, with the builder still holding all 399.6 MB. The command was not wrong; it cleared the store it was pointed at, which was empty. Pointing buildx at the builder by name cleared it in 0.656 seconds, and removing the builder outright did the same job in 0.199.
# wrong store: this reclaims 0 B while a buildx builder holds the cache
docker builder prune -a -f
# right store: name the builder
docker buildx prune --builder mybuilder -a -f
docker buildx rm mybuilderTwo commands that clear nothing
The first is docker build --no-cache. It tells one build to ignore what is cached; it deletes nothing, and the build then writes a fresh set of entries on top of the old ones. We measured the store before and after: 180.7 MB became 289.9 MB. If your goal was free space, that command moved you 109.2 MB in the wrong direction.
The second is an until filter set too wide. docker builder prune --filter until=24h reclaimed 0 B on our runner, correctly, because every entry had been written minutes earlier. Rerunning it as until=1s reclaimed 109.1 MB. The filter means "older than", and on a machine that builds constantly the entries you want gone are usually the newest ones.
| What you ran | Store before | Store after | Reclaimed |
|---|---|---|---|
docker build --no-cache | 180.7 MB | 289.9 MB | none, it added |
docker builder prune --filter until=24h -a -f | 289.9 MB | 289.9 MB | 0 B |
docker builder prune --filter until=1s -a -f | 289.9 MB | 180.7 MB | 109.1 MB |
Cap it instead of clearing it
On a machine you keep, a size ceiling beats a cron job that deletes everything, because the point of the cache is to still be there tomorrow. Docker garbage collects on its own already: Docker Desktop ships its builder with a keep-storage default of 20 GB, and for a BuildKit builder the documented defaults are a reserved space of 10 percent of the disk or 10 GB, whichever is lower, and a maximum used space of 60 percent of the disk or 100 GB, whichever is lower.
When those defaults are wrong for your disk, set the ceiling rather than pruning on a timer. docker buildx prune takes --max-used-space and --min-free-space, and docker builder prune takes --keep-storage, so a scheduled job can trim to a number instead of to nothing.
# keep the cache, cap it
docker buildx prune --max-used-space 20GB -f
# older than a week, and only that
docker builder prune --filter until=168h -fWhat we ran, so you can disagree with it
One script, job-l.sh, run once on a Latchkey latchkey-small runner on 20 September 2026, committed under content/repro/timings/clear-docker-build-cache/ with its unedited job-l.log and a status file carrying the job id, the runner size, the exit code and the timestamps. Every size on this page is a line of docker system df or docker buildx du in that log, not an estimate.
The caveats. One pass per command, so the sub-second durations carry runner noise and are worth reading as "fast" rather than as digits. The sizes are specific to one three-stage Node image built from the same fixture as the other scripts in this batch, the one job-k.log counts at 414 packages, so treat the shape as the finding and not the megabytes. And the builder comparison used a builder we created in the job, which is what docker/setup-buildx-action does for you; a builder that already existed would hold more, not less.
Frequently asked questions
Why does docker builder prune report 0 B reclaimed?
docker-container builder, which is what docker/setup-buildx-action creates, keeps its cache in its own volume, and on our runner docker builder prune -a -f reported 0 B reclaimed while that builder held 399.6 MB. Run docker buildx du --builder <name> to find it, then prune that builder by name.Does docker build --no-cache clear the build cache?
--no-cache build. Use it to force a clean rebuild, and prune separately if the goal is disk space.How do I clear the Docker build cache on a GitHub Actions runner?
What is the difference between docker system prune and docker builder prune?
docker builder prune touches only build cache. docker system prune also removes stopped containers, unused networks, dangling images, and with -a --volumes a great deal more, which is why people report it freeing space they did not expect. If the Build Cache row is the large one, the narrower command is the one you want.Related guides
References
- Docker Docs: docker builder prune reference (verified 2026-09-20)
- Docker Docs: docker buildx prune reference (verified 2026-09-20)
- Docker Docs: build cache garbage collection (verified 2026-09-20)
- Docker Docs: build drivers and the docker-container driver (verified 2026-09-20)
- Docker documentation
- Docker build cache
- GitHub Actions documentation