Reading docker system df: where Docker disk usage actually lives
You run docker system df but the numbers do not add up to what df -h reports. Maybe the Build Cache row is empty while /var/lib/docker/buildkit/ consumes tens of gigabytes. Maybe RECLAIMABLE is high but docker system prune barely frees space because overlay2 layer sharing masks the real unique cost. Or the daemon returns Error response from daemon when the disk is already full. This guide shows how to read docker system df precisely, what it hides, and how to triage the real consumers on the host.
What this means
docker system df is the authoritative CLI breakdown of Docker-managed disk space, but it is not a complete host filesystem audit. It reports four categories: Images, Containers, Local Volumes, and Build Cache. Each row shows TOTAL count, ACTIVE count, SIZE, and RECLAIMABLE space. The RECLAIMABLE column estimates space that could be freed by pruning unused objects of that type.
However, the command has blind spots. With BuildKit enabled (the default since Docker 18.09), the Build Cache row only surfaces classic builder cache. BuildKit cache lives under /var/lib/docker/buildkit/ and is invisible to docker system df. Container logs written by the json-file driver are stored inside /var/lib/docker/containers/<id>/ but are not itemized as a separate category. Inode exhaustion and Docker Desktop VM disk limits are also outside the scope of this command.
Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| BuildKit cache bloat | Build Cache row is zero or small, but /var/lib/docker/buildkit/ is huge | docker buildx du |
| Layer sharing confusion | RECLAIMABLE is low despite many unused images | docker system df -v SHARED SIZE vs UNIQUE SIZE |
| Unbounded container logs | Containers row grows slowly, host disk drops fast | find /var/lib/docker/containers/ -name "*-json.log" -exec ls -lh {} + |
| Orphaned volumes | Volumes row shows high SIZE with low ACTIVE count | docker volume ls cross-referenced with running containers |
| Inode exhaustion | docker system df shows plenty of bytes, but writes fail with “no space left on device” | df -i /var/lib/docker |
| Docker Desktop VM limit | macOS host df looks fine, but docker system df returns an error | Docker Desktop Resources settings |
Quick checks
# Summary of the four categories
docker system df
# Verbose per-item breakdown (warning: slow on large hosts)
docker system df -v
# Custom table for dashboards or scripts
docker system df --format 'table {{.Type}}\t{{.Size}}\t{{.Reclaimable}}'
# JSON output for programmatic parsing
docker system df --format json <!-- TODO: verify `--format json` is supported; may require Go template -->
# BuildKit cache, which system df does NOT show
docker buildx du <!-- TODO: verify `--verbose` flag if included -->
# Preview classic builder cache reclaimable space
docker builder prune --dry-run <!-- TODO: verify `--dry-run` is supported -->
# Inode utilization on the Docker data partition
df -i /var/lib/docker
# Log file sizes, not itemized by system df
find /var/lib/docker/containers/ -name "*-json.log" -exec ls -lh {} +
# Direct filesystem triage by subsystem
du -sh /var/lib/docker/overlay2/ /var/lib/docker/volumes/ /var/lib/docker/containers/ /var/lib/docker/buildkit/
How to diagnose it
- Start with the summary and verbose views. Run
docker system dfto see the four categories. Rundocker system df -vto inspect SHARED SIZE and UNIQUE SIZE per image. If SHARED SIZE is large, layers are heavily shared; removing one image may not free much space. - Check for the BuildKit blind spot. If the Build Cache row is absent or suspiciously small, run
docker buildx du. If this reports significant cache thatdocker system dfmissed, BuildKit is the hidden consumer. - Inspect container logs directly.
docker system dfdoes not itemize log files. Runfind /var/lib/docker/containers/ -name "*-json.log" -exec ls -lh {} +. Large logs here explain disk growth that the Containers row understates. - Verify volume usage and orphaning. Run
docker system df -vand review the Volumes section. Cross-check withdocker volume ls. Named volumes persist after container removal. A volume mounted by a stopped container is not reclaimable; verify which volumes are actually in use. - Check inode utilization. Run
df -i /var/lib/docker. If inodes are exhausted, the daemon may return “no space left on device” even when bytes are available. This often happens with millions of small files in overlay2 metadata or unrotated logs. - Correlate with the host filesystem. On Docker Desktop,
docker system dfreports VM-internal usage. Check the disk limit in Docker Desktop settings. On Linux, ensure/var/lib/dockeris on the filesystem you are monitoring. - If
docker system dfitself returnsError response from daemon, the daemon may be unable to allocate disk space to answer the query. This is a symptom of severe disk pressure. Use host-level commands likedf -handduto proceed.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
| Docker disk usage total and growth rate | Capacity planning and early warning before cascade failure | >75% used or >1GB/day sustained growth |
BuildKit cache size (docker buildx du) | BuildKit cache is invisible to docker system df and can dominate disk usage on CI runners | Tens of GB reported by buildx but not by system df |
| Container log file sizes | json-file logs are not itemized by docker system df and grow without bound if unconfigured | Single log file >1GB or total log space >20% of Docker data directory |
| Inode utilization on Docker data partition | Inode exhaustion blocks writes before byte exhaustion | >80% inode usage on the filesystem holding /var/lib/docker |
Daemon responsiveness during system df | A failing df command indicates the daemon is choking on disk pressure | Error response from daemon or timeout >5s |
Fixes
If BuildKit cache is the hidden consumer
Run docker buildx du to confirm. Remove it with docker builder prune. To preview what would be removed, run docker builder prune --dry-run . Do not rely on docker system prune for BuildKit cache.
If classic images, containers, or volumes dominate
Use targeted pruning instead of a blanket docker system prune, which is dangerous in production. Remove dangling images with docker image prune. Remove unused images older than a threshold with docker image prune -a --filter "until=48h". Remove stopped containers with docker container prune. Remove unused volumes explicitly with docker volume prune. Each of these is safer than docker system prune -f because the scope is narrower.
If container logs dominate
Truncate an active log file safely with truncate -s 0 /var/lib/docker/containers/<id>/<id>-json.log. This frees space without restarting the container. Then configure log rotation in /etc/docker/daemon.json with "log-opts": {"max-size": "10m", "max-file": "3"} and reload the daemon. Alternatively, switch to the local log driver.
If inodes are exhausted
Identify directories with many small files, typically in /var/lib/docker/overlay2/ or container log directories. Clean up stopped containers and dangling images to remove associated metadata. If overlay2 is the driver, note that du may miscount due to hard links; use docker system df for byte authority, but find and df -i for inode authority.
If Docker Desktop VM disk is full
Increase the virtual disk limit in Docker Desktop settings, or prune inside the VM. Host-level df will not reflect the VM-internal shortage.
Prevention
- Global log rotation. Configure
daemon.jsonwithlog-optsso json-file logs cannot grow unbounded. - Automated targeted cleanup. Schedule
docker image prunewith age filters instead of waiting for manual intervention. - BuildKit cache monitoring on CI runners. Run
docker buildx duon a schedule and prune BuildKit cache before it dominates the disk. - Host filesystem alerting. Set alerts for both byte usage (>70%) and inode usage (>70%) on the partition holding
/var/lib/docker. - Avoid routine
docker system prunein production. It is too broad and can remove data needed for incident analysis. Use scoped prunes. - Pin image digests and avoid tag churn. This reduces dangling image accumulation that confuses reclaimable calculations.
How Netdata helps
- Correlate Docker disk usage charts with host disk and inode utilization to detect when
docker system dfblind spots are filling the partition. - Monitor disk growth rate to catch pressure before the daemon fails to respond.
- Track per-container resource signals alongside the system-wide disk view to identify which workloads are generating writable layer or log growth.
Related guides
- If the daemon stops responding while you are checking disk usage, see Docker daemon not responding: how to troubleshoot a hung dockerd.
- If disk exhaustion has already caused commands to freeze, see Docker commands hang: docker ps, inspect, and exec freezes.
- For full disk recovery procedures, see Docker disk space full: how to troubleshoot /var/lib/docker.
- If containers are restarting due to resource pressure, see Docker container keeps restarting: causes, checks, and fixes and Docker exit code 137: OOMKilled or SIGKILL?.
- For resource-specific container issues, see Docker container high CPU usage: causes and fixes, Docker container high memory usage: how to diagnose it, and Docker container memory leak: how to find one and prove it.
- For network issues inside containers, see Docker DNS not working inside containers.
- For health check failures that may correlate with disk pressure, see Docker container running but unhealthy: how to diagnose health check failures.
- For CPU throttling that can occur alongside resource pressure, see Docker CPU throttling: the hidden cause of container latency.
- For containers that exit immediately after start, which can happen when writable layers cannot be created, see Docker container exits immediately: how to diagnose it.





