✦ For everyone, free.

Practical knowledge for real and everyday life

Home

16.3.2.1 Unused Image Bloat

A focused guide to Unused Image Bloat, connecting core concepts with practical Docker and container operations.

Unused image bloat is the gradual, often unnoticed accumulation of old, no-longer-needed image versions and dangling intermediate layers on a host, a slower-moving but extremely common contributor to disk pressure compared to a single dramatic event, and one that specifically results from the routine, expected pattern of repeatedly building and pulling new image versions without ever cleaning up the ones that came before.

Why this accumulates so naturally

Every time an image is rebuilt with the same tag, the previous image with that tag is not deleted, it simply becomes untagged, while its layers remain on disk in full, since Docker has no built-in mechanism that automatically removes a superseded image version just because a newer one now occupies the same tag:

docker build -t my-api:latest .
docker build -t my-api:latest .
docker images -a
REPOSITORY   TAG       IMAGE ID
<none>       <none>    8a1f3c9b2e7d
my-api       latest    3f29a8c1d8e2

The first build's resulting image is still present, now shown with <none> for both repository and tag, often called a "dangling" image, fully intact on disk and consuming exactly as much space as it did when it was the current, tagged version.

Identifying dangling images directly

Dangling images are specifically filterable, which makes locating them, and confirming how much space they collectively occupy, straightforward:

docker images -f dangling=true
docker images -f dangling=true --format "{{.Size}}" | paste -sd+ | bc

On a host that has been building the same image repeatedly over an extended period without cleanup, this can reveal a surprisingly large amount of space tied up entirely in images that are no longer referenced by any tag and serve no purpose other than consuming disk space.

Tagged but no longer used images

Beyond dangling images, a host can also accumulate images that are still tagged, but simply no longer needed, an old application version retained after a deployment moved on, or a base image pulled for a one-off test that was never actually removed afterward:

docker images
REPOSITORY   TAG       SIZE
my-api       1.2.0     420MB
my-api       1.3.0     425MB
my-api       1.4.0     430MB

These tagged images do not show up in a dangling-image filter, since they are not dangling by definition, which means identifying them for cleanup requires a more deliberate review of which versions are actually still needed (for rollback purposes, perhaps) versus which can reasonably be removed.

Shared layers complicate naive size estimates

Because Docker images share underlying layers when they have identical content, the sum of each individual image's reported size is not the same as the actual total disk space those images collectively occupy, since shared base layers are stored once and referenced by multiple images rather than duplicated:

docker system df -v

This more detailed breakdown shows actual, deduplicated space usage per image more accurately than naively summing the size column from docker images, which is useful for understanding the real impact of removing a specific image, since removing one of several images sharing a common base layer frees considerably less space than that image's own reported size would suggest, since the shared layer remains needed by the others.

Removing dangling and unused images safely

Dangling images are almost always safe to remove outright, since by definition nothing currently references them by any tag:

docker image prune

For tagged but no-longer-needed images, a more deliberate review before removal avoids accidentally discarding something still wanted for rollback or reference purposes:

docker images my-api
docker rmi my-api:1.2.0

Reviewing the full list of tagged versions for a given repository and removing specific ones explicitly, rather than relying on a blanket pruning command, preserves intentional control over exactly which historical versions remain available.

Build cache as a related, often larger contributor

Distinct from images themselves, the build cache (intermediate layers retained specifically to accelerate future builds) can also accumulate substantially over time, particularly for projects with frequent builds and many distinct dependency or source code states cached along the way:

docker builder du
docker builder prune --filter "until=168h"

Pruning build cache older than a specific age, rather than removing all of it indiscriminately, retains recent cache entries likely to still produce a useful cache hit on the next build while reclaiming space from genuinely stale entries unlikely to ever be reused.

Establishing a retention policy rather than reactive cleanup

Rather than only addressing unused image bloat once it becomes a noticeable disk pressure problem, a deliberate retention policy, how many historical versions of an image to keep, how old a dangling image needs to be before automatic removal, scheduled and applied consistently, prevents the slow accumulation from ever reaching a problematic level in the first place:

0 3 * * 0 docker image prune -af --filter "until=168h"

A weekly scheduled prune targeting only images unused for at least a week balances reclaiming space against the risk of removing something that might still be wanted for a near-term rollback or comparison.

Registry-side accumulation as a related but distinct concern

It is worth noting that unused image bloat on a local host is a separate concern from accumulation within a container registry, which has its own storage costs and its own retention policy considerations, typically configured at the registry level rather than through anything run on an individual Docker host; cleaning up local images does not affect what remains stored in a registry, and vice versa.

docker images
gcloud artifacts docker images list registry.example.com/my-api

Common mistakes

  • Assuming dangling images are harmless simply because they appear "untagged," rather than recognizing they occupy exactly as much disk space as any other image layer.
  • Naively summing the size column from docker images to estimate total disk usage, missing how shared layers between images reduce actual total consumption below that naive sum.
  • Removing tagged image versions in bulk without reviewing whether any are still needed for rollback purposes.
  • Treating build cache as a fixed, one-time concern rather than something that continues accumulating and needs its own periodic pruning.
  • Only addressing unused image bloat reactively once disk pressure becomes noticeable, rather than establishing a scheduled, deliberate retention policy from the outset.

Unused image bloat accumulates gradually and predictably as a direct, expected consequence of routine building and pulling activity, and the right response is a deliberate, scheduled retention and pruning policy, distinguishing dangling images (generally safe to remove freely) from tagged historical versions (worth a more deliberate review) and treating build cache as its own, separately managed category of accumulation.