Your build stops with No space left on device, docker pull fails with write /var/lib/docker/...: no space left on device, or docker run won't even create a container. The disk that holds Docker's data is full. The Docker pruning docs say unused images, containers, volumes and build cache "are generally not removed unless you explicitly ask Docker to do so." A host that builds and deploys every day fills up over weeks until something breaks.
This guide fills a Docker 29.9.3 data disk on purpose, finds what used the space and frees it with the right command for each part. All output below comes from that lab run on November 12, 2026. You are not alone with this one. On that day the Stack Overflow question Docker complains about "no space left on device", how to clean up? had 770,358 views, How to clear the logs properly for a Docker container? had 961,251 and How can I remove old and unused Docker images? had 957,579.
The short version:
- Run
df -h /var/lib/dockeranddocker system dfto see how full the disk is and what Docker counts. - In the lab, old builds sat in the build cache, not in images.
docker image prunefreed 0B anddocker builder prunefreed 839MB. - Container logs don't show up in
docker system df. Find them withdu, and cap them withmax-sizeandmax-fileindaemon.json. - If
df -hshows free space but Docker still says no space left, rundf -i. You are out of inodes. - Never delete folders inside
/var/lib/dockerby hand. Use the prune commands.
Table of contents
Why Docker runs out of disk space
Docker keeps everything under one data directory, /var/lib/docker by default. That covers images, the build cache, container writable layers, volumes and container logs. On a fresh Docker Engine 29 install with the containerd image store, the daemon docs say image contents and container snapshots live in /var/lib/containerd instead, so check that path too. The lab's Docker hosts run their own containerd, so everything landed under /var/lib/docker/containerd.
This map shows each thing that fills the disk, how to see it and how to free it:
What the errors look like
The lab built the same myapp:latest tag four times. Each build wrote a 200 MB file, the kind of thing a real app does when it bundles assets or dependencies. The fourth build failed inside the RUN step:
0.412 dd: error writing '/opt/assets-4.bin': No space left on device
ERROR: failed to build: failed to solve: process "/bin/sh -c dd if=/dev/urandom of=/opt/assets-$VERSION.bin bs=1M count=200 status=none" did not complete successfully: exit code: 1
With the disk at 100%, a pull and a plain docker run failed as well, each with a different path in the message:
The path tells you where Docker was writing, but the fix is the same. Free space on the filesystem behind /var/lib/docker.
Check the disk before you delete anything
Start with two commands on the Docker host:
df -h /var/lib/docker
docker system df
df -h shows the filesystem, so it counts everything. docker system df splits Docker's share into images, containers, local volumes and build cache, with a RECLAIMABLE column for what no running container uses. If the numbers don't add up to what df shows, look at container logs next. Docker doesn't count them here.
Free space from the build cache
This was the biggest surprise in the lab. After those four builds, docker image ls -a listed only one myapp image, and docker image ls --filter dangling=true was empty. The old versions were not images anymore. They were build cache:
Prune the build cache
docker builder prune removes build cache that no image uses. In the lab it took the disk from 100% to 48%:
docker builder prune -f
# Total: 839MB
Add -a to remove all build cache, including records an existing image still shares. Your next build then starts from scratch. To keep some cache for faster builds, Docker 29's prune has --reserved-space, --max-used-space and --min-free-space flags. In the lab, docker builder prune -f --max-used-space 100MB removed 419.5MB of unused cache and kept the records the current image still used.
The classic overlay2 store behaves a little differently. On the box's own Docker Engine 26.1.5, two builds of the same tag left the old one as a <none> image. docker image prune removed that image but reported 0B, because its 209.7MB of layers moved into the build cache. docker builder prune was still the command that gave the space back.
Let BuildKit clean up on its own
You can make the build cache trim itself. The Docker build garbage collection docs configure it in daemon.json for the default builder:
{
"builder": {
"gc": {
"enabled": true,
"defaultKeepStorage": "500MB"
}
}
}
A second lab host started with this setting built four versions of the same image. Right after the builds, the cache was 1.691GB and the disk was at 90%. Within two minutes, without any command, the cache dropped to 851.5MB and the disk to 51%. Garbage collection runs in the background, so it won't save a build that is already failing, but it stops the slow creep.
Fix container logs that fill the disk
Logs are the item that docker system df doesn't show. The lab ran a container that printed 2.5 million health check lines, a mild version of a chatty app running for weeks. docker system df reported 157.3MB for containers, and all of that was the writable layer of a separate batch container. The chatty container's log file alone was 278.7M:
Find and empty a large log
Each container's log path is in docker inspect. To list the biggest logs on the host:
du -h /var/lib/docker/containers/*/*-json.log | sort -h | tail -5
To empty one log right now, truncate it in place:
truncate -s 0 $(docker inspect -f '{{.LogPath}}' chatty)
In the lab that set the file to 0 bytes without restarting the container. Don't use rm for this. When the lab deleted a 222.9M log with rm, df didn't change at all, because Docker still had the file open. The space only came back after docker stop. BMitch, in the accepted answer on SO 42510002, warns against truncating too: "The external log deletion could happen while docker is writing json formatted data to the file, resulting in a partial line, and breaking the ability to read any logs from the docker logs cli." Truncate when you have to, then set up rotation so you don't have to again.
Rotate logs with max-size and max-file
The json-file driver docs list max-size as "Defaults to -1 (unlimited)", so logs grow forever unless you set it. Per container:
docker run -d --log-opt max-size=10m --log-opt max-file=3 alpine:3.24.4 ...
The same 2.5 million lines in the lab left three files of 2.1M, 9.5M and 9.5M. In Compose, use the logging key. The lab's service got {json-file map[max-file:3 max-size:10m]} in docker inspect:
services:
app:
image: alpine:3.24.4
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
To make it the default for every new container, put it in /etc/docker/daemon.json and restart Docker. The values must be strings:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
A lab host started with this file gave a new container the same 10m and 3 file limits without any flags. The docs are clear that existing containers keep their old settings, so recreate them with docker compose up -d --force-recreate or docker rm and docker run.
The local log driver is the other option. Per the local driver docs it keeps 100MB per container by default and compresses rotated files. In the lab, --log-driver local with no other options held the same output in 20.1M. Tools that read the JSON files directly, such as some log shippers, won't understand its format.
Clean up images, containers and volumes safely
Each of these has its own prune command. They differ in how much they delete by default, so it pays to know which flag removes what.
Prune stopped containers and unused images
Stopped containers keep their writable layer on disk. docker ps -a -s shows the size of each one. In the lab, a batch job that wrote a 150 MB export to /tmp and exited kept 157MB on disk until it was removed. docker container prune removes all stopped containers. Anything a stopped container wrote outside a volume is gone after that, so copy it out with docker cp first if you need it.
docker image prune without flags only removes dangling images, the untagged <none> ones. To remove every image that no container uses, add -a:
docker image prune -a -f
The pruning docs describe -a as removing "all images without at least one container associated to them", and a stopped container counts. Prune containers first, then images. Anything -a removes comes back with docker pull.
Volumes need extra care
Volumes hold your data, so Docker is careful with them. The docker volume prune reference says: "By default, it only removes anonymous volumes." The lab checked both kinds. docker volume prune -f removed an anonymous volume of 41.94MB and left the named pgdata volume alone. docker volume prune -a -f deleted pgdata as well.
Before you run -a, list what would go and make sure no database lives there:
docker volume ls --filter dangling=true
docker system df -v
A volume counts as used while any container references it, running or stopped. Once docker compose down removes the containers, nothing references the volume and -a will delete it. If you're cleaning up after a Postgres upgrade, read upgrade PostgreSQL 17 to 18 in Docker first so you don't delete the old data before the new one works.
docker system prune combines several of these commands. Its own warning lists what it removes: "all stopped containers", "all networks not used by at least one container", "all dangling images" and "unused build cache". In the lab it deleted two stopped containers and reclaimed 157.3MB. docker system prune -a also removes every unused image and reclaimed another 759.7MB. Neither touched the named volume.
Don't delete overlay2 by hand
It's tempting to rm -rf the biggest folder you find. BMitch's accepted answer on Is it safe to clean docker/overlay2/ puts it plainly: "Deleting this can result in data loss and possibly stop the engine from running." The lab deleted the snapshot directories of the containerd store on a test host. docker image ls still listed alpine:3.24.4, but docker run failed with failed to stat parent: stat .../snapshots/1/fs: no such file or directory. Removing the image with docker rmi and pulling it again fixed it.
Fix no space left on device when df shows free space
Sometimes df -h reports plenty of free space and Docker still fails. Check inodes, the filesystem's count of how many files it can hold. A lab host with a 1 GB disk capped at 30,000 inodes ran a build that created 40,000 small files:
Read df -i
The build failed at file 28,886 with the same No space left on device message. df -h said 13% used. df -i said 100%. Pruning the failed build's cache freed the inodes, and usage dropped to 2%. Images with huge node_modules trees or package caches use inodes fast, so clean caches in the same RUN step that creates them.
Give Docker a bigger disk
If the disk is simply too small for your workload, move Docker's data to a bigger one. Set data-root in daemon.json:
{
"data-root": "/mnt/docker-data"
}
A fresh lab host started with that setting reported /mnt/docker-data in docker info --format '{{.DockerRootDir}}'. Docker starts with an empty data directory there, so stop Docker and copy the old directory across first if you want to keep your images and volumes. On a system using the containerd image store, the daemon docs note that data-root does not move /var/lib/containerd. That location is set with root in /etc/containerd/config.toml.
For other errors that stop containers from starting, see Docker exec format error, port is already allocated and permission denied on docker.sock. If you run Elasticsearch, vm.max_map_count too low covers the host setting it needs before it starts.
FAQ
How do I see what is using disk space in Docker?
Run docker system df for images, containers, volumes and build cache, and docker system df -v for each item. Add du -h /var/lib/docker/containers/*/*-json.log for logs, which docker system df doesn't count. See SO 30604846.
Why doesn't docker image prune free any space?
Without -a it only removes dangling images. In the lab, old builds of a tag sat in the build cache instead, so docker image prune reclaimed 0B and docker builder prune reclaimed 839MB.
How do I clear the logs of a Docker container?
Run truncate -s 0 $(docker inspect -f '{{.LogPath}}' NAME) for a quick fix, then set max-size and max-file so it doesn't happen again. Deleting the file with rm doesn't free space until the container stops. See SO 42510002.
Is it safe to delete /var/lib/docker/overlay2?
No. Deleting folders inside /var/lib/docker breaks the images and containers that use them. In the lab docker run failed until the image was pulled again. Use docker system prune and the other prune commands instead. See SO 46672001.
Does docker system prune delete volumes?
Not by default. In the lab docker system prune -a -f left the named pgdata volume in place. docker volume prune removes anonymous volumes, and only docker volume prune -a removes named ones. The pruning docs cover each command.
Why does Docker say no space left on device when df shows free space?
The filesystem ran out of inodes. Check with df -i /var/lib/docker. In the lab df -h showed 13% used while df -i showed 100%, and pruning the build cache fixed it.
Originally published on DeployLark: Docker No Space Left on Device: Find and Free Disk Space





Top comments (0)