Dash0 Raises $110M Series B at $1B Valuation

  • 10 min read

How to Remove Docker Images

Docker images pile up fast. Every docker pull, every rebuild, every CI run leaves layers on disk, and the Docker daemon never garbage-collects them on its own. A few months in, docker system df reports 80GB of images and you have no idea which ones you actually need.

The right command depends on what you're trying to remove: a specific image, all the dangling layers from rebuilds, or every unused image on the host. Picking the wrong one either fails with a confusing error or wipes images you wanted to keep.

This article covers the three commands you actually need, the errors you'll hit, and the cleanup gotchas that catch people during incident response. If you're also trying to understand what your containers are consuming at runtime, the Docker stats guide is a good companion read.

Remove a specific image with docker rmi

To remove a single image, pass its name (with tag) or its image ID to docker rmi:

bash
1
docker rmi nginx:1.27
1234
Untagged: nginx:1.27
Untagged: nginx@sha256:1c2c2f1c8b0e0e8a9d3f4a5b6c7d8e9f...
Deleted: sha256:fd72a16dec5b7e6aebbf8c7d9e0a1b2c3d4e5f6a...
Deleted: sha256:a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4...

The Untagged lines remove the name pointers. The Deleted lines remove the actual layer blobs from disk. If an image has multiple tags pointing at it, you'll only see Untagged lines until you remove the last tag. Then the layers get deleted.

You can also remove by image ID, which is what you'll use for dangling images that have no tag:

bash
1
docker rmi fd72a16dec5b

docker image rm is the same command. They're aliases.

"Image is being used by stopped container"

This is the most common error, and it confuses people because the container isn't running: Docker considers an image "in use" if any container references it, including stopped ones. List every container, including stopped ones, with docker ps -a, then either remove the offending container or use -f to force the image removal:

bash
12
docker rm a4f8c9e12345
docker rmi nginx:1.27

Force-removing with docker rmi -f nginx:1.27 works, but it leaves the container in a broken state where its image reference points to nothing. Removing the container first is cleaner.

"Image is referenced in multiple repositories"

If the same image ID has multiple tags, removing by ID fails:

12
Error response from daemon: conflict: unable to delete fd72a16dec5b
(must be forced) - image is referenced in multiple repositories

Remove by tag instead. Each tag removal untags one reference; the layers only get deleted when you remove the last tag pointing to that image ID:

bash
1
docker rmi nginx:1.27 nginx:latest myregistry.io/nginx:1.27

Remove dangling images with docker image prune

Dangling images are layers with no tag and no container reference. They show up as <none>:<none> in docker images output and are almost always created when you rebuild an image with the same tag the old layers stay around, orphaned.

Remove them all in one command:

bash
1
docker image prune

When you're ready, just type Y and hit enter. This is safe to run frequently. Dangling images are by definition not referenced by anything. On a development machine where you rebuild often, this alone can reclaim several gigabytes.

To also remove tagged images that no container references, add -a:

bash
1
docker image prune -a

This is more aggressive. It removes any image not currently used by a running or stopped container, including images you pulled and might want again later. On a CI runner this is what you want; on your laptop it means re-pulling base images on the next build.

You can scope the prune by age. The until filter removes images created before a given time:

bash
1
docker image prune -a --filter "until=720h" -f

That removes images older than 30 days. The -f skips the confirmation prompt, useful for cron jobs. The filter accepts Go duration strings (24h, 168h) or RFC3339 timestamps (2026-01-01T00:00:00).

One thing to know about the until filter: it's based on image creation time, not last use. An image you pulled six months ago but use every day will still get pruned if no container is running at the moment you prune.

Remove everything unused with docker system prune

When you want to clear out images, stopped containers, unused networks, and build cache in one shot:

bash
1
docker system prune -a
1234567
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all images without at least one container associated to them
- all build cache
Are you sure you want to continue? [y/N] y

Without -a, this only removes dangling images, not unused tagged ones. With -a, it removes every image not currently bound to a container.

Volumes are not pruned by default. If you also want to remove unused volumes (which means losing any data they hold), add --volumes:

bash
1
docker system prune -a --volumes

Be careful with this one. Named volumes holding databases or persistent application state get wiped if no container is currently using them. On a development machine where you've stopped a Postgres container intending to restart it later, this is how you lose your local data.

Removing every image on the host

Sometimes you need a clean slate (wiping a CI runner, resetting a development machine, debugging a Docker daemon issue) and you want every image gone. The shortest path:

bash
1
docker rmi -f $(docker images -q)

docker images -q outputs just the image IDs; the shell expands that into arguments to docker rmi. The -f is needed because images referenced by stopped containers will otherwise refuse to delete.

If you also want containers gone first (cleaner, avoids the force), remove them before the images:

bash
12
docker rm -f $(docker ps -aq)
docker rmi $(docker images -q)

Common pitfalls

docker system prune also wipes your build cache. This catches people during cleanup on a build server. The next CI run rebuilds every layer from scratch because the BuildKit cache is gone. If you want to free image space without invalidating builds, use docker image prune -a instead of docker system prune -a. The build cache lives separately and docker buildx prune manages it on its own.

Multi-platform images need explicit handling. If you've pulled a multi-arch image (common with docker pull on Apple Silicon talking to a registry that serves multiple architectures), docker rmi removes all platform variants by default. To remove just one architecture, you need docker image rm --platform linux/amd64 --force <image>. The --force is required because removing one platform variant affects the shared content. Without --force, Docker cancels the operation with a warning. The --platform flag requires the containerd image store, which is the default on fresh installs of Docker Engine 29+ and Docker Desktop 4.34+. Run docker info | grep "Storage Driver" to check, if you're on an older install that was upgraded rather than fresh-installed, you may need to enable it manually.

Watch out for shared layers when estimating reclaimed space. Layers are shared between images. If three images all use python:3.14-slim as a base, removing one of them frees only the unique layers, not the shared base. docker system df -v shows the actual unique vs. shared sizes, which is what you want when planning a cleanup.

Removing an image doesn't break running containers. A container holds a reference to its image's layers even after the image is "removed." The image disappears from docker images output, but the container keeps running on the underlying layers. The space only frees when the last container using those layers is also removed.

Final thoughts

Image cleanup is a symptom of a bigger pattern: Docker disk usage grows silently until something breaks. By the time df shows 95% on /var/lib/docker, you're already triaging during an outage. Setting up an alert on Docker host disk usage, using something like the OpenTelemetry Host Metrics Receiver, which collects CPU, memory, disk, and network stats directly from the host, and tracking image count over time turns this from an emergency into a scheduled task.

Dash0's infrastructure monitoring tracks disk usage on your Docker hosts and correlates it with container resource metrics, logs, and distributed traces in a single OTel-native backend, so you catch storage pressure before it takes down your builds. Start a free trial to monitor your Docker infrastructure with Dash0.