Skip to content
/

Remove docker images from a Container Registry using PowerShell

When using a Docker Registry, like hub.docker.com, you will not often want to delete a published version of an image. You cannot know if someone, somewhere in the world is using that specific version.

But when using a repository as part of your CI/CD pipeline, you might have lots of versions that are not used by anyone anymore. So, what if you want to clean the repository automatically?

In this article, I will show how the delete images by tag, using PowerShell and the Docker Registry HTTP API V2. This API is implemented by registries, like the Azure Container Registry service.


In the examples, we will have two variables, Credentials and Name.

  • $Credentials is the username and password to authenticate to the repository.
  • $Name is the name of the image, like ubuntu or energy164/gw2pvo, without the tag part.

Get all image tags

First, retrieving all the existing tags for the image.

As the tags/list response can consist of multiple pages, using the FollowRelLink switch of the Invoke-RestMethod cmdlet will automatically stack the results of all pages together for us.

$tags = Invoke-RestMethod `
            -Authentication Basic `
            -Credential $DockerCredential `
            -Uri "https://lorem.azurecr.io/v2/$Name/tags/list" `
            -FollowRelLink `
        | Select-Object -ExpandProperty tags

Filter tags

With the list of tags, we can select which tags we want to remove. If you use some form of versioning in your tags, like SemVer, you can easily filter them using the Version class. This ensures 2.0 is smaller than 10.0, which is better than performing a textual comparison, as that would reason otherwise.

$tags | ? { [Version]$_ -lt [Version]'2.0.0' } | % {
  $tag = $_

  # More code here later
}

Sadly, we cannot just remove the tagged version directly. We can only delete an image using the digest as the reference.

Retrieve the digest

To get the digest, we can request details of the specific tag. The digest will be part of the response headers under the key Docker-Content-Digest. To get access to the response headers we will not use the Invoke-RestMethod here, but the Invoke-WebRequest cmdlet instead. And as we only need the headers, we can reduce network traffic by specifying the HTTP HEAD method.

There is also a caveat. The Docker-Content-Digest header only holds the correct value if we specify an Accept header with the value application/vnd.docker.distribution.manifest.v2+json.

$response = Invoke-WebRequest `
            -Method Head `
            -Authentication Basic `
            -Credential $DockerCredential `
            -Uri "https://lorem.azurecr.io/v2/$Name/manifests/$tag" `
            -Headers @{ 'Accept' = 'application/vnd.docker.distribution.manifest.v2+json' }

$digest = $response.Headers['Docker-Content-Digest']

Delete the image

And finally, we can use the HTTP DELETE method on our image with the digest connected to the tag we wanted to remove.

Invoke-WebRequest `
        -Method Delete `
        -Authentication Basic `
        -Credential $DockerCredential `
        -Uri "https://lorem.azurecr.io/v2/$Name/manifests/$digest" `
        | Select-Object -ExpandProperty StatusDescription