docker system df tells you how much space Docker is using and which category is responsible. It only reports; it removes nothing. Allow about ten minutes for a first check, spread across images, containers, volumes and build cache.
This guide follows Docker CE CLI 5:29.8.1-1~ubuntu.24.04~noble, reporting Docker version 29.8.1 on this machine. Your totals and names will differ. The command talks to the Docker daemon, so you need a working Docker context and permission to reach it. Normal inspection does not need sudo.
Check the client version and ask Docker for its option list. Both are ordinary, read-only commands:
$ docker --version
Docker version 29.8.1, build 4a63305
$ docker system df --help
Usage: docker system df [OPTIONS]
Show docker disk usage
Now run the default report:
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 32 28 14.79GB 36.76MB (0%)
Containers 48 48 223.3MB 0B (0%)
Local Volumes 17 5 12.22GB 11.93GB (97%)
Build Cache 132 0 6.266GB 6.218GB
Start with the largest SIZE, then compare its ACTIVE and RECLAIMABLE figures. In the sample above, local volumes and build cache deserve attention, while containers show no reported reclaimable space. That still does not make an inactive-looking volume safe to remove: a volume can hold the only copy of application data, and a build cache can shorten later builds.
ACTIVE is not a backup or ownership decision, just Docker's usage classification, and its meaning depends on the object type. Keep the report as evidence, then inspect the detailed rows before changing anything.
Checkpoint: save a baseline if you are chasing a disk alert. Redirecting output creates a report but does not touch Docker:
$ docker system df > docker-system-df-before.txt
$ test -s docker-system-df-before.txt && echo 'baseline saved'
baseline saved
Add --verbose, or its short form -v, for per-image, per-container, volume and build-cache detail:
$ docker system df --verbose
Images space usage:
REPOSITORY TAG IMAGE ID CREATED SIZE SHARED SIZE UNIQUE SIZE CONTAINERS
...
Containers space usage:
CONTAINER ID IMAGE COMMAND LOCAL VOLUMES SIZE CREATED STATUS NAMES
...
Local Volumes space usage:
VOLUME NAME LINKS SIZE
...
Build cache usage: 6.266GB
...
Your report will list your own objects. For images, SHARED SIZE is data shared with another image and UNIQUE SIZE is data attributed only to that one. The displayed image SIZE is the virtual total of those two, so do not add up every image row and call the result extra disk usage. Containers show their writable-layer size, not their full image size.
The detailed volume section earns its keep whenever a summary is dominated by local volumes. A volume with zero links can still be deliberate: kept for a stopped service or a later restore. Check a volume's owner, backup status and contents with the relevant application tooling before running any removal command.
Use the documented JSON format when another program needs stable fields rather than aligned columns:
$ docker system df --format json
{"Active":"28","Reclaimable":"36.76MB (0%)","Size":"14.79GB","TotalCount":"32","Type":"Images"}
{"Active":"48","Reclaimable":"0B (0%)","Size":"223.3MB","TotalCount":"48","Type":"Containers"}
{"Active":"5","Reclaimable":"11.93GB (97%)","Size":"12.22GB","TotalCount":"17","Type":"Local Volumes"}
{"Active":"0","Reclaimable":"6.218GB","Size":"6.266GB","TotalCount":"132","Type":"Build Cache"}
This is one JSON object per line, not one JSON array. Process it as newline-delimited JSON, and treat fields such as Size and Reclaimable as display strings with units, not raw byte counts you can sort directly. Docker may report a reclaimable percentage in parentheses for some categories and omit it for others.
For a quick human-readable filter, use a Go template. The shell's single quotes keep the braces and dollar signs intact:
$ docker system df --format '{{.Type}}: {{.Size}} ({{.Reclaimable}} reclaimable)'
Images: 14.79GB (36.76MB (0%) reclaimable)
Containers: 223.3MB (0B (0%) reclaimable)
Local Volumes: 12.22GB (11.93GB (97%) reclaimable)
Build Cache: 6.266GB (6.218GB reclaimable)
Your values will differ. If a script needs JSON, prefer --format json over parsing the aligned table.
If Docker says it cannot connect to the daemon, check the selected context and daemon reachability without changing anything:
$ docker context show
default
$ docker info > /dev/null && echo 'daemon reachable'
daemon reachable
$ docker system df
Your context name may differ. An unreachable daemon is a service or context problem, not evidence that disk usage is zero. A permission error for the Docker socket means your account lacks access under the local security policy: use an approved group or administrative procedure, or run the read-only command with sudo only when that is the established policy. Adding your account to the Docker group grants broad control over the host, so do not treat that as a casual workaround.
If you are checking a remote context, remember the report describes the daemon that context selects, not necessarily the filesystem of the shell where you typed the command. Confirm the context before recording or comparing totals.
docker system df has no cleanup mode. Do not reach for a reclaim command just because the report shows a big percentage. Image, container, volume and builder pruning each have different matching rules and can remove data or cached work, and volume deletion is particularly risky because application data may live there.
Before any later cleanup, record the baseline, identify exact object IDs or names, confirm a backup and check whether a service is using the data. Schedule anything service-affecting separately. If an approved cleanup produces an unexpected result, stop and compare a fresh docker system df --verbose report with the baseline; do not repeat a broad prune just to make the numbers look tidy. This guide itself changes no Docker state, so none of its own commands need an undo.
docker system df completed against the intended Docker context.--verbose to identify candidate objects before considering cleanup.