I want to clean up all of my Docker images that aren't being used, directly or indirectly, by my current containers. Docker provides the docker image prune
command for this, but I can't get it to remove exactly what I want. If I use that command without -a
, it removes too little: it leaves behind all tagged images, even if no container uses them. If I use that command with -a
, it removes too much: it removes images that are parents of images that aren't being removed. Here's a table explaining the problem:
Used by container | Has child image | Has tag | Removed without -a | Removed with -a | I want to remove
Yes | Yes/No | Yes/No | No | No | No
No | Yes | Yes/No | No | Yes | No
No | No | Yes | No | Yes | Yes
No | No | No | Yes | Yes | Yes
Is there some other flag for this command (such as something with --filter
), or some other command or sequence of commands, that lets me remove just what I want to remove?
EDIT: David Maze points out in the comments that the images I'm referring to are merely being untagged, not removed entirely. Given that, here's an updated phrasing of the question: How can I make docker image prune -a
not untag images that it's not actually going to remove?
You can remove an image with docker rmi command, passing the name of the image you want to remove. This will remove the image. Sometimes when testing and developing, some images become dangling, which means untagged images. They can always be safely removed to free disk space.
Remove all images All the Docker images on a system can be listed by adding -a to the docker images command. Once you're sure you want to delete them all, you can add the -q flag to pass the image ID to docker rmi : List: docker images -a.
I often wondered the same thing, so I came up with this solution:
# create unique array of image ids (including all descendants) used by running containers
get_active_image_history()
{
for image_name in $(docker ps --format={{.Image}})
do
docker history -q $image_name
done | sort -u
}
# iterate over each image id in your system, and output the ids that should be deleted
# conceptually similar to an intersection of two arrays
get_unused_image_ids()
{
image_history=$(get_active_image_history)
for image_id in $(docker image ls -q)
do
if [[ ! "${image_history[@]}" =~ "${image_id}" ]]; then
echo "$image_id"
fi
done | sort -u
}
docker_clean()
{
image_ids=$(get_unused_image_ids)
if [ -z "$image_ids" ]; then
>&2 echo "Error: no unused images found"
return 1
fi
if [[ "$1" = "-y" ]]; then
echo y | docker container prune > /dev/null
docker image rm $(get_unused_image_ids)
return 0
fi
echo -e "Warning! this will remove all stopped containers, and the following images:\n"
image_ids=$(get_unused_image_ids)
echo "REPOSITORY TAG IMAGE ID CREATED SIZE"
docker image ls | grep "$image_ids"
echo
read -p "Continue (y/n)?" cont
if [ "$cont" = "y" ]; then
echo y | docker container prune > /dev/null
docker image rm $image_ids
else
echo "aborting..."
fi
}
No very elegant, but it works well enough for use in development. You can pass a -y
flag to silence the warning prompt.
There are several cases that I came across where the prompt incorrectly showed an image that was currently used, and attempted to remove it, but was then caught by the docker daemon which results in misleading noise similar to this:
Error response from daemon: conflict: unable to delete <image id> (must be forced) -
image is being used by stopped container <container id>
In all cases, only the correct images were removed, but this might vary depending on your system. YMMV
TLDR; not production safe
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With