How to Clean Up Docker: Images, Containers, and Volumes
Docker never reclaims anything on its own. Every image you pulled, every container you stopped and every volume you forgot is still on the disk, and one command removes almost all of it.
Docker keeps things because it cannot know what you still want. Stopped containers are kept so you can read their logs, images are kept so a rerun does not re-pull them, and anonymous volumes outlive the container that created them.
The result is a disk that fills gradually and then all at once. Here is what is taking the space, and the smallest command that gets it back.
Find out what is using the space
docker system df # summary by type
docker system df -v # every image, container and volume, individuallyThe one command most people want
# Everything not currently in use, including images no container references
docker system prune -a --volumesOr clean one thing at a time
| Command | Removes | Keeps |
|---|---|---|
docker container prune | All stopped containers | Running containers and their images |
docker image prune | Dangling images only, the untagged leftovers of rebuilds | Every tagged image |
docker image prune -a | Every image no container references | Only images in use right now |
docker volume prune | Volumes no container references | Volumes attached to any container, running or not |
docker network prune | Networks with no containers attached | Default networks |
Filters, for when prune -a is too much
# Only images older than a week
docker image prune -a --filter "until=168h"
# Keep anything you have labelled
docker image prune -a --filter "label!=keep=true"
# One specific image
docker rmi ghcr.io/acme/app:old-tagWhat this does not cover
- The build cache. It is not an image and prune commands aimed at images will not free it. See clearing the Docker build cache.
- Space inside a running container. That belongs to the container and is only freed when the container is removed.
- On Docker Desktop, the virtual disk does not always shrink after a prune even though the space is free inside it. That is a separate problem with a separate fix.
Measure before you optimise
Pipeline optimisation usually targets the step people assume is slow. Get the real per-step timings first, because the answer is frequently dependency install or a cold cache rather than the build itself.
# per-job timings for the last 20 runs
gh run list --limit 20 --json databaseId,conclusion,createdAt,updatedAt \
--jq '.[] | "\(.conclusion)\t\(.createdAt)\t\(.updatedAt)"'
# per-step timing inside one run
gh run view <run-id> --log | grep -E "^\S+\s+.*Run |##\[group\]" | head -40Frequently asked questions
How do I clean up Docker and free disk space?
docker system df to see where the space has gone, then docker system prune -a --volumes to remove everything not currently in use. If that is too aggressive, prune images, containers and volumes separately, or add --filter "until=168h" so only older items are removed.What is the difference between docker image prune and docker image prune -a?
-a form removes every image that no container references, including tagged images you may still want. The first is safe and usually frees little; the second frees a lot and can cost you a re-pull.Does docker system prune delete my data?
--volumes, and it never touches running containers. With --volumes it does remove unused volumes, which is where a development database usually lives, so check docker volume ls before adding that flag.How do I remove all Docker images at once?
docker rmi -f $(docker images -aq) removes every image, forcefully. docker image prune -a is the better command in almost every case, because it stops short of images something is actually using.Can I automate Docker cleanup?
docker system prune -af --filter "until=168h" is the common form: it reclaims anything untouched for a week and leaves recent work alone. On CI runners that are reused between jobs this is worth setting up before disk-full errors start failing builds.