How to delete images from a private docker registry?

DockerDocker Registry

Docker Problem Overview


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?

Docker Solutions


Solution 1 - Docker

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?

Solution 2 - Docker

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

Step 1: Listing catalogs

You can list your catalogs by calling this url:

http://YourPrivateRegistyIP:5000/v2/_catalog

Response will be in the following format:

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

You can list tags of your catalog by calling this url:

http://YourPrivateRegistyIP:5000/v2/<name>/tags/list

Response will be in the following format:

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

}

You can run this command in docker registry container:

curl -v --silent -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X GET http://localhost:5000/v2/<name>/manifests/<tag> 2>&1 | grep Docker-Content-Digest | awk '{print ($3)}'

Response will be in the following format:

sha256:6de813fb93debd551ea6781e90b02f1f93efab9d882a6cd06bbd96a07188b073

Run the command given below with manifest value:

curl -v --silent -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -X DELETE http://127.0.0.1:5000/v2/<name>/manifests/sha256:6de813fb93debd551ea6781e90b02f1f93efab9d882a6cd06bbd96a07188b073

Step 4: Delete marked manifests

Run this command in your docker registy container:

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

Here is my config.yml

root@c695814325f4:/etc# cat /etc/docker/registry/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

Solution 3 - Docker

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

Working usage: https://github.com/byrnedo/docker-reg-tool

Edit: The manifest <reference> above can be retrieved from requesting to

GET /v2/<name>/manifests/<tag>

and checking the Docker-Content-Digest header in the response.

Edit 2: You may have to run your registry with the following env set:

REGISTRY_STORAGE_DELETE_ENABLED="true"

Edit3: You may have to run garbage collection to free this disk space: https://docs.docker.com/registry/garbage-collection/

Solution 4 - Docker

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"}

Solution 5 - Docker

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. Login to 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.

Solution 6 - Docker

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 get github.com/fraunhoferfokus/deckschrubber
$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.

Solution 7 - Docker

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

Solution 8 - Docker

Briefly;

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

    docker inspect :/:

    > 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>:/v2//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

Solution 9 - Docker

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.

Solution 10 - Docker

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

Solution 11 - Docker

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/

Solution 12 - Docker

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

Solution 13 - Docker

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.

Solution 14 - Docker

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

Solution 15 - Docker

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLeoView Question on Stackoverflow
Solution 1 - DockerKonrad KleineView Answer on Stackoverflow
Solution 2 - DockerYavuz SertView Answer on Stackoverflow
Solution 3 - DockerbyrnedoView Answer on Stackoverflow
Solution 4 - DockerLarry CaiView Answer on Stackoverflow
Solution 5 - DockerhirroView Answer on Stackoverflow
Solution 6 - DockerYan FotoView Answer on Stackoverflow
Solution 7 - DockerBMitchView Answer on Stackoverflow
Solution 8 - DockerfgulView Answer on Stackoverflow
Solution 9 - DockerisalgueiroView Answer on Stackoverflow
Solution 10 - DockerIgor RizhyiView Answer on Stackoverflow
Solution 11 - DockerVidar LangseidView Answer on Stackoverflow
Solution 12 - DockerShrijan TiwariView Answer on Stackoverflow
Solution 13 - DockerIlya KrigouzovView Answer on Stackoverflow
Solution 14 - DockeralonanaView Answer on Stackoverflow
Solution 15 - DockerNoumenonView Answer on Stackoverflow