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?
How to delete images from a private docker registry?
-
4Accepted answer is not correct anymore (though definitely very good). You can remove images using DELETE /v2/<name>/manifests/<reference>: docs.docker.com/registry/spec/api/#deleting-an-image– Michael ZelenskyJun 5 '20 at 7:02
-
@MichaelZelensky i tried a 'tag' for <reference> but that didn't work for me. which <reference> should i use/how to obtain it? (using this registry: registry.hub.docker.com/_/registry from October 2020 )– HoutmanOct 23 '20 at 7:37
-
using: GET -m DELETE -C <user/pw> https://<a remote host>:81/v2/<my-repo>/manifests/latest response: {"errors":[{"code":"UNSUPPORTED","message":"The operation is unsupported."}]}– HoutmanOct 23 '20 at 7:43
-
-
1@Houtman, have a look here: stackoverflow.com/questions/62211899/… Oct 24 '20 at 12:56
-
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?
-
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?– LeoAug 28 '14 at 20:19
-
2I've edited my answer with a workaround that does the job for me. I hope it helps. 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!– LeoAug 29 '14 at 20:56
-
@KonradKleine how do you make the images graph out of the list of images you get from the registry? Dec 15 '14 at 22:46
-
13Garbage collecting has finally arrived to the Registry v2.4.0 docs.docker.com/registry/garbage-collection Apr 14 '16 at 7:32
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
-
1The 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 -
3to delete an image, you should run the registry container with REGISTRY_STORAGE_DELETE_ENABLED=true parameter.– AdilNov 6 '17 at 8:05
-
3I 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.– SteveWNov 19 '18 at 21:26
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/
-
3Some 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
-
-
Getting unsupported error. I have given proper digest value as the reference.– WinsterFeb 22 '18 at 6:36
-
-
1@JamesThomasMoon1979 updated with instructions about getting the reference hash.– byrnedoAug 9 '18 at 11:23
-
12on V2 too and getting error
{"errors":[{"code":"UNSUPPORTED","message":"The operation is unsupported."}]}
Oct 31 '18 at 21:30
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:
-
Login to the container
docker exec -it registry sh
-
Define variables matching your container and container version:
export NAME="google/cadvisor" export VERSION="v0.24.1"
-
Move to the the registry directory:
cd /var/lib/registry/docker/registry/v2
-
Delete files related to your hash:
find . | grep `ls ./repositories/$NAME/_manifests/tags/$VERSION/index/sha256`| xargs rm -rf $1
-
Delete manifests:
rm -rf ./repositories/$NAME/_manifests/tags/$VERSION
-
Logout
exit
-
Run the GC:
docker exec -it registry bin/registry garbage-collect /etc/docker/registry/config.yml
-
If all was done properly some information about deleted blobs is shown.
-
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 "– OduvanJul 23 '19 at 10:38
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"}
-
4Please, see my answer below for an explanation why this command doesn't help. Aug 28 '14 at 14:21
-
3You 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
-
3it 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.– LeoSep 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
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.
-
1
deckschrubber
is pretty good - very easy to install (single binary), and lets you delete images by name (with regex match) as well as age.– RichVelSep 5 '17 at 13:23 -
-
-
-
Unfortunately, it will not remove images which don't have tags. So if you keep pushing images on a single tag, it will only check the one which is tagged, not the other ones.– KrystianMar 25 '19 at 10:06
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
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.
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
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/
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
-
1Could you elaborate a bit more on how to use this fine script? Oct 3 '19 at 16:29
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
.
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
Not the answer you're looking for? Browse other questions tagged docker docker-registry or ask your own question.
-
-
1Thank 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 gettingMANIFEST_UNKNOWN
error. Even though it's somewhere in their documentation(docs.docker.com/registry/spec/api) it's very confusing.– Tomasz WNov 27 '19 at 14:10