feat(system): add nerdctl system df - #5130
Open
ekalinin wants to merge 2 commits into
Open
Conversation
`nerdctl images` marks an image as in use by resolving the image name stored on the container, which follows the tag wherever it points now. After `nerdctl tag` moves a tag onto another image, the container gets attributed to an image it never ran: the U indicator lands on the wrong row. Record the image target digest on the container at creation time, in a new nerdctl/image-digest label, and use it for the in-use lookup. Containers created before this label existed, or created outside nerdctl, are still resolved by name; an unparsable value falls back the same way rather than dropping the container from the set. This also matters for the ACTIVE and RECLAIMABLE columns of `nerdctl system df`, which build on the same lookup. Signed-off-by: Eugene Kalinin <e.v.kalinin@gmail.com>
ekalinin
force-pushed
the
feat/system-df
branch
2 times, most recently
from
August 7, 2026 19:51
3b7058e to
38b701c
Compare
Add `nerdctl system df`, the equivalent of `docker system df`, reporting how much disk
space the images, containers and local volumes of the current namespace use, plus the
BuildKit build cache.
The sizes follow the Docker v29 definitions for the containerd image store:
- the size of an image is the content present in the content store plus its unpacked
snapshots, which is the same value `nerdctl images` shows as DISK USAGE,
- the SIZE of the Images row counts every snapshot and every blob once, so it is the
space really taken on disk rather than the sum of the image sizes,
- an image is active when a container references it, and only the part of an unused
image that no other image shares is reclaimable,
- containers contribute their read-write layer only, and everything not running is
reclaimable,
- a volume is active when a container mounts it, counted once however many paths it is
mounted at, as the LINKS column of Docker is a count of containers,
- a build cache record is reclaimable when it is neither in use nor shared.
An image reports when it was built, read from the config of the manifest the platform
matcher of this host selects: the platforms of an index are not necessarily built
together, and the creation time of the local record only says when the image was pulled
or tagged. The record time stays as the fallback for the images that state nothing.
Both `--format` and `-v/--verbose` are supported, including the Docker `table TEMPLATE`
format, e.g. `table {{.Type}}\t{{.Size}}`: the literal \t and \n are expanded, the chosen
columns get a header that names them whatever the template does to the values below, and
the rows stay aligned. That helper lives in pkg/formatter so that the other commands,
which all share this gap, can adopt it. Identifiers are shortened for the table output
only, so that a custom format stays usable to look a resource up.
The work is split the way `system prune` already is: `pkg/cmd/system` orchestrates,
and each kind of resource is measured by its own package.
Closes containerd#3942
Signed-off-by: Eugene Kalinin <e.v.kalinin@gmail.com>
ekalinin
force-pushed
the
feat/system-df
branch
from
August 7, 2026 20:18
38b701c to
5f44c03
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add
nerdctl system df, the equivalent ofdocker system df: the disk space taken by the images,containers and local volumes of the current namespace, plus the BuildKit build cache.
The size definitions
The issue was blocked on a question: Docker v29 changed how an image size is computed (#5027), so
what should
system dfreport? The formulas below were derived from moby/moby v29 with thecontainerd image store (
daemon/disk_usage.go,daemon/containerd/service.go) and docker/cli v29(
cli/command/formatter/disk_usage.go).Σ (Size - SharedSize)of the unreferencedΣ SizeRw(read-write layer only)Σ SizeRwof the non-activeΣ SizeΣ Sizeof the unreferencedΣ SizeΣ Sizeof those neither in use nor sharedSIZEis not the sum of the image sizes: what images share is counted once, sothe row reports the space really taken on disk.
nerdctl imagesalready shows asDISK USAGE. The two commands agree. Docker sizes an image by walkingits manifests, so the index listing them is counted in the row total while no single image is
charged for it: an unused image is never quite 100% reclaimable.
names (
repo:tag,repo@digest, plus the config digest underk8s.io), which would otherwisemultiply every count and every size.
createdof its config, not the local record time, which only says when itwas pulled or tagged and would show an old image as brand new. For a multi-platform image it is
the config of the manifest the host platform matcher selects, since the platforms of an index are
not necessarily built together; the record time stays as the fallback.
Output
-v/--verboseprints the four detail sections with Docker'sSHARED SIZE/UNIQUE SIZEcolumns.--formatalso accepts the Dockertable TEMPLATEform, e.g.table {{.Type}}\t{{.Size}}: theliteral
\tand\nare expanded, the columns get an aligned header, and the labels go throughdocker/cli's
HeaderFunctions, sotable {{lower .Type}}still names the columnTYPE. Nonerdctl command supported
table TEMPLATEso far, so the helper lives inpkg/formatterfor theothers to adopt. Identifiers are shortened for table output only, matching
Format.IsTable(), sothat a custom format stays usable to look a resource up.
Notes
host of that namespace, and shows as zeros when BuildKit is down, so the shape of the output does
not depend on which daemons happen to be up.
system prunealready is:pkg/cmd/systemorchestrates, each kind of resource ismeasured by its own package.
gets one), the summary and verbose rendering,
table TEMPLATE, the platform selection of thebuild time, and the parsing of
buildctl du. The integration test runs in a private namespaceand checks the counts after a pull, a run and a stop, plus the three output formats.
Depends on #5125
The first commit belongs to #5125, which pins the image a container runs to its digest.
system dfuses the same in-use lookup: without it a retagged image lands in the wrong row and its layers
count as reclaimable while a container still holds them. Only the second commit is this PR.
Closes #3942