Перейти к содержимому. | Перейти к навигации

Персональные инструменты

How to delete images from a private docker registry?

https://stackoverflow.com/questions/25436742/how-to-delete-images-from-a-private-docker-registry

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?

Follow

13 Answers

135

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?

Follow

96

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>,
    ...
  ]
}

Step 2: Listing tags for related catalog

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>,
    ...
]

}

Step 3: List manifest value for related 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
Follow
  • 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. May 18 '17 at 0:26
  • 3
    to delete an image, you should run the registry container with REGISTRY_STORAGE_DELETE_ENABLED=true parameter.
    – Adil
    Nov 6 '17 at 8: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 Nov 15 '17 at 7: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

81

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/

Follow
  • 3
    Some other answers here show how to extract manifest reference hash. This is necessary to translate a tag into something to pass to DELETE. May 18 '17 at 0:24

19

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.

Follow
  • 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? 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

19

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"}
Follow
  • 4
    Please, see my answer below for an explanation why this command doesn't help. 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. 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. 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 5 '14 at 8: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. Nov 11 '14 at 18:10

12

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.

Follow

1

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

Follow

1

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.

Follow

1

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
Follow

0

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/

Follow

0

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
Follow

0

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.

Follow

0

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
Follow

 

 

Not the answer you're looking for? Browse other questions tagged or ask your own question.

  • 1
    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(docs.docker.com/registry/spec/api) it's very confusing. Nov 27 '19 at 14:10