How can I delete a mistakenly added tag from an image in a private Docker registry? The option -rmi doesn't seem to work for remote images in Docker 1.9.1.

user@ubuntu-user:~$ docker --version  
Docker version 1.9.1, build a34a1d5

user@ubuntu-user:~$ docker search myregistry:5000/user/image

user@ubuntu-user:~$ docker rmi myregistry:5000/user/image:20160119-20160120
Error response from daemon: could not find image: no such id: myregistry:5000/user/image:20160119-20160120
Error: failed to remove images: [myregistry:5000/user/image:20160119-20160120]

There seems to be no simple method to do remove images from the registry as of today and looks like its a feature for registry 2.1 milestone.

One of the options what we have today with this not working

anovil@ubuntu-anovil remove-registry]$ curl -X DELETE localhost:5000/v2/alpine/manifests/v1
{"errors":[{"code":"UNSUPPORTED","message":"The operation is unsupported."}]}
[anovil@ubuntu-anovil remove-registry]$ 

is to manually remove it from the registry itself. Just to avoid removing wrong file accidentally, I tested it with this script from github. I take no guarantee on how this script works (though I checked it quickly before I tested it).

So, I made a test and that seems to work :)

[1] I presume that, you are running a registry with docker itself.

[anovil@ubuntu-anovil remove-registry]$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
88f8e1a1d7a7        registry:2          "/bin/registry /etc/d"   37 minutes ago      Up 37 minutes>5000/tcp   registry
[anovil@ubuntu-anovil remove-registry]$ 

[2] I created a minimal Dockerfile with just FROM alpine content and created alpine:v1 and pushed to my private registry running on localhost:5000. Querying it from registry, it returned as expected.

[anovil@ubuntu-anovil remove-registry]$ curl -X GET localhost:5000/v2/alpine/tags/list
[anovil@ubuntu-anovil remove-registry]$ 

[3] Then I log into the registry with docker exec and checked the disk usage before I do my experiment

root@88f8e1a1d7a7:/# du -sch /var/lib/registry/                     
2.5M    /var/lib/registry/
2.5M    total

[4] After returning back to my host I copied a heavy file (mongodb.tgz) into my container and created a built and pushed version v2.

[anovil@ubuntu-anovil remove-registry]$ docker build -t localhost:5000/alpine:v2 .
Sending build context to Docker daemon 61.99 MB
Step 1 : FROM alpine
 ---> 2314ad3eeb90
Step 2 : COPY mongodb.tgz /mongodb.tgz
 ---> d7c7645a3fe2
Successfully built d7c7645a3fe2
[anovil@ubuntu-anovil remove-registry]$ docker push localhost:5000/alpine:v2
The push refers to a repository [localhost:5000/alpine] (len: 1)
d7c7645a3fe2: Pushed 
5ff05309724e: Image already exists 
v2: digest: sha256:7bea1ec2910170bd88412b622aee6129791673cf1fd8c0e1e34f15ec26428774 size: 4467
[anovil@ubuntu-anovil remove-registry]$ 

[5] After checking the size again in the registry, it has increased to 62MB:

root@88f8e1a1d7a7:/# du -sch /var/lib/registry/                                                                               
62M /var/lib/registry/
62M total

[6] In order to run delete_docker_registry_image, you need to get the script into the container which is hosting the registry, one option to do this is with curl. Also, this script requires jq.

root@88f8e1a1d7a7:/# apt-get update && apt-get install -y curl jq

[7] Run the script, try with --dry-run option first and do not forget the version tag (v2 in this case), there is also a good -h

root@88f8e1a1d7a7:/# delete_docker_registry_image --image alpine:v2 --dry-run
DRY_RUN: would have deleted tag directory: repositories/alpine/_manifests/tags/v2
DRY_RUN: would have deleted manifest revision: repositories/alpine/_manifests/revisions/sha256/7bea1ec2910170bd88412b622aee6129791673cf1fd8c0e1e34f15ec26428774
DRY_RUN: would have deleted directory: blobs/sha256/e2/e2cc9aed084e01fa5cf93c09121035ac4d712113425ae68b678c28591beec5c6
DRY_RUN: would have deleted directory: blobs/sha256/7a/7ada67971e952e353ab14d8f9bdd4e41e4c41099b05a5da09f2700b51d93908a
DRY_RUN: would have deleted directory: blobs/sha256/7b/7bea1ec2910170bd88412b622aee6129791673cf1fd8c0e1e34f15ec26428774
DRY_RUN: would have deleted layer metadata directory: repositories/alpine/_layers/sha256/e2cc9aed084e01fa5cf93c09121035ac4d712113425ae68b678c28591beec5c6
root@88f8e1a1d7a7:/# delete_docker_registry_image --image alpine:v2          

[8] And voila !!

root@88f8e1a1d7a7:/# du -sch /var/lib/registry/                     
2.5M    /var/lib/registry/
2.5M    total

Just recently ran in to this myself, but then thought, why delete, I'll just re-release an older version:

docker push my/image:1.0.0
docker push my/image:1.0.1 # This is broken

docker tag  my/image:1.0.0 my/image:1.0.2
docker push my/image:1.0.2

The broken image will still be there, but it's unlikely anyone will use it as there's a "newer" version available. Obviously it's better to fix forward but in a pinch this is a fast solution.

If the reason you want to delete it is that it has secrets or something exposed you didn't want getting out, the other solutions are better, but assume whatever it was is now known and change it (passwords, keys, whatever).

  • 1
    So the required storage space just grows and grows as more images get pushed but never purged? – emmdee Mar 28 '18 at 18:24
  • @emmdee well I mean yeah... but remember, disk-wise it's only storing the deltas between revisions, just like git. for an image that ends up being 1 GB, that doesn't mean every time you release a new version it's another 1 GB being used on disk. – Michael Butler Jul 27 '18 at 19:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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