How to Delete Docker Images: Commands, Options, and What to Consider First
Docker images can pile up fast. Every docker pull, every build, every failed experiment leaves something behind. Over time, unused images eat into disk space — sometimes gigabytes worth — and a cluttered local registry makes it harder to find what you actually need. Deleting Docker images is straightforward once you understand the commands, but the right approach depends on your environment, workflow, and what's currently running.
What Docker Images Actually Are
Before deleting anything, it helps to understand what you're working with. A Docker image is a read-only template used to create containers. Images are built in layers — each instruction in a Dockerfile adds a layer, and those layers are cached and reused across images.
This layered system is efficient, but it also means images aren't always as independent as they look. Deleting one image doesn't necessarily remove all its layers if other images share them. Docker handles this automatically — it won't delete a layer that's still referenced elsewhere.
Images also have two identifiers you'll encounter constantly:
- Image ID — a unique hash (e.g.,
sha256:3f4a2b...) - Repository and tag — a human-readable name (e.g.,
ubuntu:22.04)
An image can have multiple tags pointing to the same ID, which matters when you're trying to remove something cleanly.
The Core Command: docker rmi
The primary command for removing images is:
docker rmi <image_name_or_id> You can reference an image by name, tag, or ID:
docker rmi ubuntu:22.04 docker rmi 3f4a2b8c1d9e To remove multiple images at once, list them space-separated:
docker rmi image1 image2 image3 Important: Docker will refuse to delete an image if a container — even a stopped one — is still using it. You'll see an error like image is being used by stopped container. You either need to remove that container first (docker rm <container_id>) or use the force flag.
Force Removal
docker rmi -f <image_id> The -f flag forces deletion even if containers reference the image. Use this carefully — forcing removal of an image tied to a running container can cause unexpected behavior. In development environments it's generally fine; in production, it warrants more caution.
Listing Images Before You Delete 🗂️
Always know what you're working with:
docker images This shows repository name, tag, image ID, creation date, and size. For a more complete view including intermediate layers:
docker images -a To find dangling images — untagged images no longer referenced by any tagged image, typically leftover build artifacts:
docker images -f dangling=true Bulk Removal: Cleaning Up Efficiently
Remove All Dangling Images
docker image prune This removes all dangling (untagged, unreferenced) images. It's the safest bulk operation because it only targets images with no active references.
Remove All Unused Images
docker image prune -a Adding -a removes all images not currently used by at least one container — including tagged ones. This is more aggressive. If you pull an image for reference but haven't built a container from it yet, it goes.
Remove Everything with docker system prune
docker system prune This goes broader — stopped containers, unused networks, dangling images, and build cache all get cleared. Add -a to include all unused images:
docker system prune -a With --volumes, it also removes anonymous volumes. This is a significant cleanup operation and will prompt for confirmation by default.
Filtering by Age or Label
Docker's --filter flag gives you more control:
docker image prune -a --filter "until=72h" This removes unused images older than 72 hours. You can also filter by label, which is useful in environments where images are tagged with metadata during CI/CD pipelines.
Key Variables That Change Your Approach
| Factor | Impact on Deletion Strategy |
|---|---|
| Running containers | Can't delete images in use without force |
| Shared base images | Layers shared across images won't fully delete |
| CI/CD pipelines | Aggressive pruning may break cached build layers |
| Docker Compose projects | Compose-managed images may need docker compose down --rmi all |
| Multi-stage builds | Intermediate images may appear dangling but were intentional |
| Registry vs. local | docker rmi only removes locally — remote registry images need separate management |
Docker Compose Specifics
If you're working with Compose, the standard docker rmi command still works, but there's a more integrated option:
docker compose down --rmi all This stops containers, removes them, and deletes associated images in one step. Using --rmi local instead only removes images that don't have a custom tag — useful when you want to keep pulled base images but discard locally built ones.
What Doesn't Get Deleted (And Why)
A few things catch people off guard: 🤔
- Images referenced by any container (running or stopped) are protected unless forced
- Named volumes are not touched by
image prune— onlysystem prune --volumeshandles those - Remote registry images are completely unaffected —
docker rmiis purely local - Build cache survives
image prunebut is cleared bybuilder pruneorsystem prune
This distinction between local image storage and remote registries matters a lot in team environments. Removing an image locally doesn't affect what teammates can pull, and it doesn't free up registry storage.
How Your Setup Shapes the Right Approach
A solo developer doing local experimentation has very different needs than a team running builds through a CI/CD pipeline. Prune commands that are perfectly safe on a personal laptop could disrupt build caching in a pipeline, where those cached layers represent real time savings. Similarly, a production server warrants far more caution than a development machine — forced removal or aggressive pruning without checking what's running first introduces real risk.
The frequency of your cleanup matters too. Regular, lighter pruning (dangling images only) tends to be more manageable than periodic nuclear-option cleanups that require auditing everything afterward.
How aggressive you should be with deletion depends entirely on what's running, what's shared, and what that disk space is actually worth in your specific environment.