Search code examples
docker

Selectively prune docker images by tag


If there are multiple image tags for a used image, docker image prune -a will keep the alphabetically last tag. Please see a reproducible example at the bottom.

Is there a way to influence which tags will be kept? Ideally, I would like docker image prune -a to keep all image-tag combinations that are in use (in use includes stopped containers).

Reproducible example (careful as it will delete images from your docker system):

# docker image prune -a keeps hellow-world:x

docker pull hello-world
docker tag hello-world:latest hello-world:x
docker tag hello-world:latest hello-world:a
docker run hello-world:x
docker run hello-world:a
docker image prune -a
# Reset

docker system prune -a
# docker image prune -a keeps hellow-world:z

docker pull hello-world
docker tag hello-world:latest hello-world:x
docker tag hello-world:latest hello-world:z
docker run hello-world:x
docker run hello-world:z
docker image prune -a

Solution

  • I don't know of a way to do it natively with the docker cli, but a combination of awk/sed can help.

    myhost:~# docker pull hello-world
    myhost:~# docker tag hello-world:latest hello-world:x
    myhost:~# docker tag hello-world:latest hello-world:z
    myhost:~# docker run hello-world:x
    myhost:~# docker run hello-world:z
    

    Instead of using prune, we'll generate the list of images and then send to docker rmi instead:

    myhost:~# docker ps -a --format '{{.Image}}' > ps_images
    myhost:~# cat ps_images
    hello-world:z
    hello-world:x
    
    myhost:~# docker images --format '{{.Repository}}:{{.Tag}}' | sort -r
    hello-world:z
    hello-world:x
    hello-world:latest
    

    Those are the images as they might be pruned, where (due to the sort -r) hello-world:z should be retained since it is the lexicographically last image for hello-world. If we filter out images found in the preceding docker ps -a command, we should preserve both :z and :x, which in this case means removing :latest.

    myhost:~# docker images --format '{{.Repository}}:{{.Tag}}' | sort -r \
      | awk -F: '{arr[$1]++;print(arr[$1],$1 ":" $2);}' \
      | sed -nE 's/^([^1] |1[^ ]+ )(.*)/\2/gp' | grep -f ps_images -v
    hello-world:latest
    

    We can send this directly to docker rmi:

    myhost:~# docker images --format '{{.Repository}}:{{.Tag}}' | sort -r \
      | awk -F: '{arr[$1]++;print(arr[$1],$1 ":" $2);}' \
      | sed -nE 's/^([^1] |1[^ ]+ )(.*)/\2/gp' | grep -f ps_images -v \
      | xargs docker rmi
    Untagged: hello-world:latest
    myhost:~# docker images
    REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
    hello-world   x         feb5d9fea6a5   15 months ago   13.3kB
    hello-world   z         feb5d9fea6a5   15 months ago   13.3kB
    

    Walk-through:

    • sort -r orders them with latest-first

    • awk ... numbers them by the first component of the image name, where its output is

      1 hello-world:z
      2 hello-world:x
      3 hello-world:latest
      
    • sed ... removes the 1 lines

    • grep ... removes images that we discovered from docker ps -a in the first code block (the -v is inverting the match)

    • xargs ... sends the output lines (hello-world:latest in this case) as individual arguments to docker rmi