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

CauseWhat it looks likeFirst thing to check
BuildKit cache bloatBuild Cache row is zero or small, but /var/lib/docker/buildkit/ is hugedocker buildx du
Layer sharing confusionRECLAIMABLE is low despite many unused imagesdocker system df -v SHARED SIZE vs UNIQUE SIZE
Unbounded container logsContainers row grows slowly, host disk drops fastfind /var/lib/docker/containers/ -name "*-json.log" -exec ls -lh {} +
Orphaned volumesVolumes row shows high SIZE with low ACTIVE countdocker volume ls cross-referenced with running containers
Inode exhaustiondocker system df shows plenty of bytes, but writes fail with “no space left on device”df -i /var/lib/docker
Docker Desktop VM limitmacOS host df looks fine, but docker system df returns an errorDocker 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

  1. Start with the summary and verbose views. Run docker system df to see the four categories. Run docker system df -v to 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.
  2. 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 that docker system df missed, BuildKit is the hidden consumer.
  3. Inspect container logs directly. docker system df does not itemize log files. Run find /var/lib/docker/containers/ -name "*-json.log" -exec ls -lh {} +. Large logs here explain disk growth that the Containers row understates.
  4. Verify volume usage and orphaning. Run docker system df -v and review the Volumes section. Cross-check with docker 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.
  5. 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.
  6. Correlate with the host filesystem. On Docker Desktop, docker system df reports VM-internal usage. Check the disk limit in Docker Desktop settings. On Linux, ensure /var/lib/docker is on the filesystem you are monitoring.
  7. If docker system df itself returns Error 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 like df -h and du to proceed.

Metrics and signals to monitor

SignalWhy it mattersWarning sign
Docker disk usage total and growth rateCapacity 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 runnersTens of GB reported by buildx but not by system df
Container log file sizesjson-file logs are not itemized by docker system df and grow without bound if unconfiguredSingle log file >1GB or total log space >20% of Docker data directory
Inode utilization on Docker data partitionInode exhaustion blocks writes before byte exhaustion>80% inode usage on the filesystem holding /var/lib/docker
Daemon responsiveness during system dfA failing df command indicates the daemon is choking on disk pressureError 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.json with log-opts so json-file logs cannot grow unbounded.
  • Automated targeted cleanup. Schedule docker image prune with age filters instead of waiting for manual intervention.
  • BuildKit cache monitoring on CI runners. Run docker buildx du on 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 prune in 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 df blind 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.