265

I run a private docker registry, and I want to delete all images but the latest from a repository. I don't want to delete the entire repository, just some of the images inside it. The API docs don't mention a way to do this, but surely it's possible?

Hosam Aly
  • 41,555
  • 36
  • 141
  • 182
Leo
  • 3,036
  • 3
  • 13
  • 12

17 Answers17

142

Currently you cannot use the Registry API for that task. It only allows you to delete a repository or a specific tag.

In general, deleting a repository means, that all the tags associated to this repo are deleted.

Deleting a tag means, that the association between an image and a tag is deleted.

None of the above will delete a single image. They are left on your disk.


Workaround

For this workaround you need to have your docker images stored locally.

A workaround for your solution would be to delete all but the latest tags and thereby potentially removing the reference to the associated images. Then you can run this script to remove all images, that are not referenced by any tag or the ancestry of any used image.

Terminology (images and tags)

Consider an image graph like this where the capital letters (A, B, ...) represent short image IDs and <- means that an image is based on another image:

 A <- B <- C <- D

Now we add tags to the picture:

 A <- B <- C <- D
           |    |
           |    <version2>
           <version1>

Here, the tag <version1> references the image C and the tag <version2> references the image D.

Refining your question

In your question you said that you wanted to remove

all images but the latest

. Now, this terminology is not quite correct. You've mixed images and tags. Looking at the graph I think you would agree that the tag <version2> represents the latest version. In fact, according to this question you can have a tag that represents the latest version:

 A <- B <- C <- D
           |    |
           |    <version2>
           |    <latest>
           <version1>

Since the <latest> tag references image D I ask you: do you really want to delete all but image D? Probably not!

What happens if you delete a tag?

If you delete the tag <version1> using the Docker REST API you will get this:

 A <- B <- C <- D
                |
                <version2>
                <latest>

Remember: Docker will never delete an image! Even if it did, in this case it cannot delete an image, since the image C is part of the ancestry for the image D which is tagged.

Even if you use this script, no image will be deleted.

When an image can be deleted

Under the condition that you can control when somebody can pull or push to your registry (e.g. by disabling the REST interface). You can delete an image from an image graph if no other image is based on it and no tag refers to it.

Notice that in the following graph, the image D is not based on C but on B. Therefore, D doesn't depend on C. If you delete tag <version1> in this graph, the image C will not be used by any image and this script can remove it.

 A <- B <--------- D
      \            |
       \           <version2>
        \          <latest>
         \ <- C
              |
              <version1>

After the cleanup your image graph looks like this:

 A <- B <- D
           |
           <version2>
           <latest>

Is this what you want?

Community
  • 1
  • 1
Konrad Kleine
  • 4,275
  • 3
  • 27
  • 35
  • You're right--deleting the tags didn't actually delete the associated images. Is there any way to delete the images given ssh access to the box, then? – Leo Aug 28 '14 at 20:19
  • 2
    I've edited my answer with a workaround that does the job for me. I hope it helps. – Konrad Kleine Aug 29 '14 at 11:52
  • That's exactly it--I understand that images are built incrementally, but I need a way to prune those dead branches. Thanks! – Leo Aug 29 '14 at 20:56
  • @KonradKleine how do you make the images graph out of the list of images you get from the registry? – Rafael Barros Dec 15 '14 at 22:46
  • @RafaelBarros The image "graph" is nothing more than a simple list of image IDs. Take a look at this script https://gist.github.com/kwk/c5443f2a1abcf0eb1eaa . It operates not on the Registry API but on the file system. It's just a matter of collecting all image IDs we have in a list and removing those from the list that are part of another image's ancestry. The remainder are those images that can be deleted. (I quite often use the term image and layer interchangeably.) – Konrad Kleine Dec 16 '14 at 08:25
  • 13
    Garbage collecting has finally arrived to the Registry v2.4.0 https://docs.docker.com/registry/garbage-collection/ – Markus Rexhepi-Lindberg Apr 14 '16 at 07:32
  • I check your script ... why go all through these hassle? I know the uuid value of the tag, and I know the repo name. I also know, that my image has no ancestry, (except the base ubuntu). So I can just login and 'rm -rf' the corresponding folders. And by the way, /var/lib/* is not always the location, so the script felt risky. – daparic Aug 01 '16 at 06:18
  • @daixtr I know that the script isn't perfect but it it gives a start. If you have the knowledge that you have regarding ancestry of your image you can go in an delete whatever you like but for a more systematic approach you need to go through all the hassle or upgrade to registry 2.4.0 at least as markus-lindberg pointed out. – Konrad Kleine Aug 02 '16 at 06:14
  • one doubt, even if I am restarting the raspi where registry server is setup with some images in the private repo, all the images are lost. I tried passing restart = always. – stackjohnny Dec 21 '18 at 18:30
  • There's no `registry` in my `/var/lib/docker/` dir. – x-yuri Oct 11 '22 at 22:31
130

I've faced the same problem with my registry, then I tried the solution listed below from a blog page. It works.

Do note, the deletion must be enabled for it to work. You can do it by providing a custom config, or by setting REGISTRY_STORAGE_DELETE_ENABLED=true.

Step 1: List the repositories

$ curl -sS <domain-on-ip>:5000/v2/_catalog

The response will be in the following format:

{
  "repositories": [
    <repo>,
    ...
  ]
}

Step 2: List the repository tags

$ curl -sS <domain-on-ip>:5000/v2/<repo>/tags/list

The response will be in the following format:

{
    "name": <repo>,
    "tags": [
        <tag>,
        ...
    ]
}

Step 3: Determine the digest of the target tag

$ curl -sS -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
-o /dev/null \
-w '%header{Docker-Content-Digest}' \
<domain-or-ip>:5000/v2/<repo>/manifests/<tag>

Do note, the Accept header is needed here. Without it you'll get a different value and the deletion will fail.

The response will be in the following format:

sha256:6de813fb93debd551ea6781e90b02f1f93efab9d882a6cd06bbd96a07188b073

Step 4: Delete the manifest

$ curl -sS -X DELETE <domain-or-ip>:5000/v2/<repo>/manifests/<digest>

Step 5: Garbage collect the image

Run this command in your docker registry container:

$ registry garbage-collect /etc/docker/registry/config.yml

Here is my config.yml:

version: 0.1
log:
    fields:
    service: registry
storage:
    cache:
        blobdescriptor: inmemory
    filesystem:
        rootdirectory: /var/lib/registry
    delete:
        enabled: true
http:
    addr: :5000
    headers:
        X-Content-Type-Options: [nosniff]
health:
    storagedriver:
        enabled: true
        interval: 10s
        threshold: 3
x-yuri
  • 16,722
  • 15
  • 114
  • 161
Yavuz Sert
  • 2,387
  • 2
  • 13
  • 26
  • 1
    The commands succeeded, including messages about "`Deleteing blob: /docker/...`" but it didn't change the disk space used. Using _bin/registry github.com/docker/distribution v2.4.1_. – JamesThomasMoon May 18 '17 at 00:26
  • 5
    to delete an image, you should run the registry container with REGISTRY_STORAGE_DELETE_ENABLED=true parameter. – Adil Nov 06 '17 at 08:05
  • 3
    I set "delete: enabled" value to true in /etc/docker/registry/config.yml file. For this configuration no need to set REGISTRY_STORAGE_DELETE_ENABLED variable @AdilIlhan – Yavuz Sert Nov 15 '17 at 07:31
  • re: step 3, the "Docker-Content-Digest" must be in the header? (not seeing it in the curl output). Should I be able to see the results of marking a manifest before deleting the marked manifest(s) to know I successfully marked it? It seems to give me a 202 response no matter what I send it. – SteveW Nov 19 '18 at 21:26
  • the `Docker-Content-Digest` part should be in lowercase (tested on docker engine v18.09.2) i.e. `curl -v --silent -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X GET http://localhost:5000/v2//manifests/ 2>&1 | grep docker-content-digest | awk '{print ($3)}'` – Muhammad Abdurrahman May 30 '19 at 10:15
  • In the step 3 the part with `awk` must be in that way `awk '{print $2}'` and add the option `-I` to the `curl` command !! – Felipe Pereira Aug 02 '19 at 15:18
  • 3
    Thank you, this is the first answer that worked for me. I was missing `Accept: application/vnd.docker.distribution.manifest.v2+json` header and kept on getting `MANIFEST_UNKNOWN` error. Even though it's somewhere in their documentation(https://docs.docker.com/registry/spec/api/) it's very confusing. – Tomasz W Nov 27 '19 at 14:10
  • 2
    Steps 3 doesn't return any output (not even an errors !!), any suggestions ! – McLan Jun 16 '21 at 08:34
  • @TomaszW Good you thing you pointed out the header. Somehow I overlooked this every time I was reading around. Saved me. – Blaisem Jul 14 '23 at 02:56
90

The current v2 registry now supports deleting via DELETE /v2/<name>/manifests/<reference>.

See: https://github.com/docker/distribution/blob/master/docs/spec/api.md#deleting-an-image

The <reference> can be taken from the Docker-Content-Digest header of a GET /v2/<name>/manifests/<tag> request (do note that the Accept: application/vnd.docker.distribution.manifest.v2+json header is needed for this request).

A script that makes use of it: https://github.com/byrnedo/docker-reg-tool

For it to work deletion must be enabled (REGISTRY_STORAGE_DELETE_ENABLED=true).

And to really free the disk space you need to run garbage collection.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
byrnedo
  • 1,415
  • 10
  • 12
22

This is really ugly but it works, text is tested on registry:2.5.1. I did not manage to get delete working smoothly even after updating configuration to enable delete. The ID was really difficult to retrieve, had to login to get it, maybe some misunderstanding. Anyway, the following works:

  1. Enter the container

     docker exec -it registry sh
    
  2. Define variables matching your container and container version:

     export NAME="google/cadvisor"
     export VERSION="v0.24.1"
    
  3. Move to the the registry directory:

     cd /var/lib/registry/docker/registry/v2
    
  4. Delete files related to your hash:

     find . | grep `ls ./repositories/$NAME/_manifests/tags/$VERSION/index/sha256`| xargs rm -rf $1
    
  5. Delete manifests:

     rm -rf ./repositories/$NAME/_manifests/tags/$VERSION
    
  6. Logout

     exit
    
  7. Run the GC:

     docker exec -it registry  bin/registry garbage-collect  /etc/docker/registry/config.yml
    
  8. If all was done properly some information about deleted blobs is shown.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
hirro
  • 671
  • 10
  • 17
  • i followed the same steps mentioned above, but when i checked the docker registry {container} disk usage it was showing same as before doing the steps... any idea why? – Savio Mathew Apr 26 '18 at 10:28
  • doesn't work. Easy to check. When you do the following steps and push the repo again it says "064794e955a6: Layer already exists " – Oduvan Jul 23 '19 at 10:38
  • This is indeed ugly. And there are better ways these days. – x-yuri Oct 11 '22 at 22:53
20

Problem 1

You mentioned it was your private docker registry, so you probably need to check Registry API instead of Hub registry API doc, which is the link you provided.

Problem 2

docker registry API is a client/server protocol, it is up to the server's implementation on whether to remove the images in the back-end. (I guess)

DELETE /v1/repositories/(namespace)/(repository)/tags/(tag*)

Detailed explanation

Below I demo how it works now from your description as my understanding for your questions.

I run a private docker registry. I use the default one, and listen on port 5000.

docker run -d -p 5000:5000 registry

Then I tag the local image and push into it.

$ docker tag ubuntu localhost:5000/ubuntu
$ docker push localhost:5000/ubuntu
The push refers to a repository [localhost:5000/ubuntu] (len: 1)
Sending image list
Pushing repository localhost:5000/ubuntu (1 tags)
511136ea3c5a: Image successfully pushed
d7ac5e4f1812: Image successfully pushed
2f4b4d6a4a06: Image successfully pushed
83ff768040a0: Image successfully pushed
6c37f792ddac: Image successfully pushed
e54ca5efa2e9: Image successfully pushed
Pushing tag for rev [e54ca5efa2e9] on {http://localhost:5000/v1/repositories/ubuntu/tags/latest}

After that I can use Registry API to check it exists in your private docker registry

$ curl -X GET localhost:5000/v1/repositories/ubuntu/tags
{"latest": "e54ca5efa2e962582a223ca9810f7f1b62ea9b5c3975d14a5da79d3bf6020f37"}

Now I can delete the tag using that API !!

$ curl -X DELETE localhost:5000/v1/repositories/ubuntu/tags/latest
true

Check again, the tag doesn't exist in my private registry server

$ curl -X GET localhost:5000/v1/repositories/ubuntu/tags/latest
{"error": "Tag not found"}
bguiz
  • 27,371
  • 47
  • 154
  • 243
Larry Cai
  • 55,923
  • 34
  • 110
  • 156
  • 4
    Please, see my answer below for an explanation why this command doesn't help. – Konrad Kleine Aug 28 '14 at 14:21
  • 3
    You delete tags but not the image data. The latter is done by the script I reference after you have deleted a tag. I also use the registry API to delete a tag. – Konrad Kleine Aug 31 '14 at 14:53
  • 3
    it is upto each registry API server's implementation, from protocol's point of view, it doesn't exist. – Larry Cai Aug 31 '14 at 14:57
  • You're right that it's up to the implementation--but I was/am looking for a way to delete image data from the canonical `registry` image. SSHing in and running a script works, even if it's not ideal. As a side note I still think it's an incomplete API--you can delete tags, but if you GET `/images` you still see all the leftover image data. – Leo Sep 05 '14 at 08:24
  • Thanks for mentioning Registry API, was looking for a way to DELETE an image in private registry. This method is good enough for me even if it's just 'untagging' without actually free up disk space. – Howard Lee Nov 11 '14 at 18:10
  • How to delete the image from Registry 2.0? – firelyu Dec 02 '15 at 10:09
  • This 'curl' method to delete tags is very helpful. Thank you good sir. – daparic Aug 01 '16 at 06:19
  • @KonradKleine you focus so much about the tag<->image difference in your answer that you actually forgot to answer what was asked. This is what LarryCai did better – Daniel Alder Dec 12 '17 at 13:00
  • I think this is the only technically correct answer in this thread. – avishayp Mar 11 '20 at 13:38
  • Note, that this is the solution for `v1`. But I have `v2`:( – phi Jan 11 '22 at 13:38
16

There are some clients (in Python, Ruby, etc) which do exactly that. For my taste, it isn't sustainable to install a runtime (e.g. Python) on my registry server, just to housekeep my registry!


So deckschrubber is my solution:

go install github.com/fraunhoferfokus/deckschrubber@latest
$GOPATH/bin/deckschrubber

images older than a given age are automatically deleted. Age can be specified using -year, -month, -day, or a combination of them:

$GOPATH/bin/deckschrubber -month 2 -day 13 -registry http://registry:5000

UPDATE: here's a short introduction on deckschrubber.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
Yan Foto
  • 10,850
  • 6
  • 57
  • 88
7

The requirement to delete all tags except latest gets complicated because the same image manifest can be pointed to by multiple tags, so when you delete a manifest for one tag, you may effectively delete multiple tags.

There are a few options to make that workable. One is to track the digest for the latest tag and only delete manifests for other digests, or you can use some different API calls to delete the tags themselves.


Regardless of how you implement this, first your registry needs to be configured to allow the delete API's. With the minimal registry:2 image, that involves starting it with an environment variable REGISTRY_STORAGE_DELETE_ENABLED=true (or the equivalent yaml config).

Then for a simple script to loop through the tags and delete, there's:

#!/bin/sh
repo="localhost:5000/repo/to/prune"
for tag in $(regctl tag ls $repo); do
  if [ "$tag" != "latest" ]; then
    echo "Deleting: $(regctl image digest --list "${repo}:${tag}") [$tag]"
    regctl tag rm "${repo}:${tag}"
  fi
done

The regctl command used here comes from regclient and the regctl tag rm logic first attempts to perform the tag delete API added recently to the distribution-spec. Since most registries haven't implemented that spec, it falls back to the manifest delete API, but it first creates a dummy manifest to overwrite the tag, and then deletes that new digest. In doing so, if the old manifest was in use by other tags, it doesn't delete those other tags.

An alternative version of the script that deletes manifests except those pointing to the latest digest looks like:

#!/bin/sh
repo="localhost:5000/repo/to/prune"
save="$(regctl image digest --list "${repo}:latest")"
for tag in $(regctl tag ls $repo); do
  digest="$(regctl image digest --list "${repo}:${tag}")"
  if [ "$digest" != "$save" ]; then
    echo "Deleting: $digest [$tag]"
    regctl manifest rm "${repo}@${digest}"
  fi
done

If you find yourself needing to create a deletion policy to automate the deleting of lots of images, I'd recommend looking at regclient/regbot from the same repo which allows you to define that policy and leave it running to continuously prune your registry.


Once the images have been deleted, you'll need to garbage collect your registry in most use cases. For example with the registry:2 image that looks like:

docker exec registry /bin/registry garbage-collect \
  /etc/docker/registry/config.yml --delete-untagged

Caution: there is an open issue with the garbage-collect utility that will delete untagged child manifests of a multi-platform image. If you are using multi-platform images, you'll need to ensure every platform specific image is tagged or avoid using the above GC command until the issue has been resolved in your deployed version.

BMitch
  • 231,797
  • 42
  • 475
  • 450
3

I am usually all for doing things with scripts, but if you are already running a registry UI container built from Joxit/docker-registry-ui, I found it easier to just opt-click the delete button in the UI and delete a page of images at a time, then garbage collect after.

Noumenon
  • 5,099
  • 4
  • 53
  • 73
2

Briefly;

1) You must typed following command for RepoDigests of a docker repo;

## docker inspect <registry-host>:<registry-port>/<image-name>:<tag>
> docker inspect 174.24.100.50:8448/example-image:latest


[
    {
        "Id": "sha256:16c5af74ed970b1671fe095e063e255e0160900a0e12e1f8a93d75afe2fb860c",
        "RepoTags": [
            "174.24.100.50:8448/example-image:latest",
            "example-image:latest"
        ],
        "RepoDigests": [
            "174.24.100.50:8448/example-image@sha256:5580b2110c65a1f2567eeacae18a3aec0a31d88d2504aa257a2fecf4f47695e6"
        ],
...
...

${digest} = sha256:5580b2110c65a1f2567eeacae18a3aec0a31d88d2504aa257a2fecf4f47695e6

2) Use registry REST API

  ##curl -u username:password -vk -X DELETE registry-host>:<registry-port>/v2/<image-name>/manifests/${digest}


  >curl -u example-user:example-password -vk -X DELETE http://174.24.100.50:8448/v2/example-image/manifests/sha256:5580b2110c65a1f2567eeacae18a3aec0a31d88d2504aa257a2fecf4f47695e6

You should get a 202 Accepted for a successful invocation.

3-) Run Garbage Collector

docker exec registry bin/registry garbage-collect --dry-run /etc/docker/registry/config.yml

registry — registry container name.

For more detail explanation enter link description here

fgul
  • 5,763
  • 2
  • 46
  • 32
2

Another tool you can use is registry-cli. For example, this command:

registry.py -l "login:password" -r https://your-registry.example.com --delete

will delete all but the last 10 images.

isalgueiro
  • 1,973
  • 16
  • 20
2

There is also a way you can remove some old images from repository just based on the date when it was created.

To do that enter your docker registry container and get the list of manifest's revisions for some specific repository:

ls -latr /var/lib/registry/docker/registry/v2/repositories/YOUR_REPO/_manifests/revisions/sha256/

The output then may be used within the request (with sha256 prefix):

curl -v --silent -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE http://DOCKER_REGISTRY_HOST:5000/v2/YOUR_REPO/manifests/sha256:OUTPUT_LINE

And of course do not forget to execute 'garbage-collect' command after that:

bin/registry garbage-collect /etc/docker/registry/config.yml
1

This docker image includes a bash script that can be used to remove images from a remote v2 registry : https://hub.docker.com/r/vidarl/remove_image_from_registry/

1

Below Bash Script Deletes all the tags located in registry except the latest.

for D in /registry-data/docker/registry/v2/repositories/*; do
if [ -d "${D}" ]; then
    if [ -z "$(ls -A ${D}/_manifests/tags/)" ]; then
        echo ''
    else
        for R in $(ls -t ${D}/_manifests/tags/ | tail -n +2); do
            digest=$(curl -k -I -s -H -X GET http://xx.xx.xx.xx:5000/v2/$(basename  ${D})/manifests/${R} -H 'accept: application/vnd.docker.distribution.manifest.v2+json'  | grep Docker-Content-Digest | awk '{print $2}' )
            url="http://xx.xx.xx.xx:5000/v2/$(basename  ${D})/manifests/$digest"
            url=${url%$'\r'}
            curl -X DELETE -k -I -s   $url -H 'accept: application/vnd.docker.distribution.manifest.v2+json' 
        done
    fi
fi
done

After this Run

docker exec $(docker ps | grep registry | awk '{print $1}') /bin/registry garbage-collect /etc/docker/registry/config.yml
Shrijan Tiwari
  • 673
  • 6
  • 17
1

Simple ruby script based on this answer: registry_cleaner.

You can run it on local machine:

./registry_cleaner.rb --host=https://registry.exmpl.com --repository=name --tags_count=4

And then on the registry machine remove blobs with /bin/registry garbage-collect /etc/docker/registry/config.yml.

Ilya Krigouzov
  • 271
  • 2
  • 16
1

Here is a script based on Yavuz Sert's answer. It deletes all tags that are not the latest version, and their tag is greater than 950.

#!/usr/bin/env bash


CheckTag(){
    Name=$1
    Tag=$2

    Skip=0
    if [[ "${Tag}" == "latest" ]]; then
        Skip=1
    fi
    if [[ "${Tag}" -ge "950" ]]; then
        Skip=1
    fi
    if [[ "${Skip}" == "1" ]]; then
        echo "skip ${Name} ${Tag}"
    else
        echo "delete ${Name} ${Tag}"
        Sha=$(curl -v -s -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X GET http://127.0.0.1:5000/v2/${Name}/manifests/${Tag} 2>&1 | grep Docker-Content-Digest | awk '{print ($3)}')
        Sha="${Sha/$'\r'/}"
        curl -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE "http://127.0.0.1:5000/v2/${Name}/manifests/${Sha}"
    fi
}

ScanRepository(){
    Name=$1
    echo "Repository ${Name}"
    curl -s http://127.0.0.1:5000/v2/${Name}/tags/list | jq '.tags[]' |
    while IFS=$"\n" read -r line; do
        line="${line%\"}"
        line="${line#\"}"
        CheckTag $Name $line
    done
}


JqPath=$(which jq)
if [[ "x${JqPath}" == "x" ]]; then
  echo "Couldn't find jq executable."
  exit 2
fi

curl -s http://127.0.0.1:5000/v2/_catalog | jq '.repositories[]' |
while IFS=$"\n" read -r line; do
    line="${line%\"}"
    line="${line#\"}"
    ScanRepository $line
done
alonana
  • 171
  • 2
  • 12
0

A script to remove all but the latest tag from an insecure registry (private, no auth):

#!/bin/sh -eu
repo=$1
registry=${2-localhost:5000}

tags=`curl -sS "$registry/v2/$repo/tags/list" | jq -r .tags[]`
tag2digest() {
    local tag=$1
    curl -sS -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
        -o /dev/null \
        -w '%header{Docker-Content-Digest}' \
        "$registry/v2/$repo/manifests/$tag"
}
latest_digest=`tag2digest latest`
digests=`echo "$tags" \
    | while IFS= read -r tag; do
        tag2digest "$tag"
        echo
    done \
    | sort \
    | uniq`
digests=`echo "$digests" \
    | grep -Fvx "$latest_digest"`
echo "$digests" \
    | while IFS= read -r digest; do
        curl -sS -X DELETE "$registry/v2/$repo/manifests/$digest"
    done

Usage:

$ ./rm-tags.sh <image> [<registry>]

After removing tags (or manifests to be more precise) run garbage collection:

$ registry garbage-collect /etc/docker/registry/config.yml

To support Docker Hub and/or auth see these answers.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
-1

This is the simplest solution that worked for my setup, using a private registry in a swarm cluster

Experimenting with whatever filter you need

docker images | grep 'your_own_filter'

tail -n +4: keep the last 3 images that got built

awk '{print $3}': will extract the 3rd column, which is the 'IMAGE ID'

Example:

docker rmi $(docker images | grep 'your_own_filter' | tail -n +4 | awk '{print $3}')
Omar
  • 8,374
  • 8
  • 39
  • 50
  • How does this delete the images from the docker registry? The `docker rmi` command only deletes images from your local docker engine. – BMitch Jul 25 '23 at 14:03