Add remote tag to a docker image
Asked Answered
E

6

46

On a private registry (myregistry.com), say I have an image tagged with 'v1.2.3'. I then push it by:

docker push myregistry.com/myimage:v1.2.3

If I want to associate another tag, say 'staging', and push that tag to my registry, I can:

docker tag myregistry.com/myimage:v1.2.3 myregistry.com/myimage:staging
docker push myregistry.com/myimage:staging

Though this works, the second docker push still runs through each image, attempting to push it (albeit skipping upload). Is there a better way just to add a remote tag?

Exsert answered 5/11, 2014 at 17:29 Comment(1)
if you have network or time concerns you can use this method: https://mcmap.net/q/373309/-how-to-tag-image-in-docker-registry-v2-duplicateVishinsky
R
33

The way you've stated, docker tag ...; docker push ... is the best way to add a tag to an image and share it.

In the specific example you've given, both tags were in the same repo (myregistry.com/myimage). In this case you can just docker push myregistry.com/myimage and by default the docker daemon will push all the tags for the repo at the same time, saving the iteration over layers for shared layers.

You can use the same process (docker tag ...; docker push ...) to tag images between repositories as well, e.g.

docker tag myregistry.com/myimage:v1.2.3 otherregistry.com/theirimage:v2
docker push otherregistry.com/theirimage
Repartee answered 5/11, 2014 at 22:4 Comment(4)
what if I want to tag only 1 image and just push a couple of them? i.e. say my local docker stores v.1.1, v.1.2 and v.1.3. I would like to tag v.1.3 with latest and push both v.1.3 and latest but leave older versions where they are?Meredith
This does not work if you have multi-platform images and the original image you are trying to re-tag is not on the local machine (i.e. you first have to pull it). This is because "docker pull" only pulls the image layers that match the local machine's platform architecture, then if you push the new tag(s) you will only push an image with a single platform architecture layer.Really
Maybe it was true at some point but it is no longer true that all tags are pushed by default. You need the -a or --all-tags option. docs.docker.com/engine/reference/commandline/push/#all-tagsGet
Like mentioned in other questions you don't need to pull whole image if you are using tools like buildx imagetools or crane. Please add notice about this to your answerAudrieaudris
R
36

You can achieve this with docker buildx imagetools create

docker buildx imagetools create myregistry.com/myimage:v1.2.3 --tag myregistry.com/myimage:staging

this will simply download the image manifest of myregistry.com/myimage:v1.2.3 and re-tag (and push) it as myregistry.com/myimage:staging

NOTE: this will also retain the multi-platform manifest list when you "re-tag" (e.g. when your image is build for both linux/arm64 and linux/amd64). Where as the conventional docker pull/push strategy will only retain the image manifest for the platform/architecture of the system you do the pull/push from.

Really answered 30/12, 2021 at 1:30 Comment(1)
Actually I like this approach. In fact, I came up with it myself. Unfortunately docker buildx imagetools create creates a manifest list (wrapping the one input), which may not be appropriate in all cases.Rebirth
R
33

The way you've stated, docker tag ...; docker push ... is the best way to add a tag to an image and share it.

In the specific example you've given, both tags were in the same repo (myregistry.com/myimage). In this case you can just docker push myregistry.com/myimage and by default the docker daemon will push all the tags for the repo at the same time, saving the iteration over layers for shared layers.

You can use the same process (docker tag ...; docker push ...) to tag images between repositories as well, e.g.

docker tag myregistry.com/myimage:v1.2.3 otherregistry.com/theirimage:v2
docker push otherregistry.com/theirimage
Repartee answered 5/11, 2014 at 22:4 Comment(4)
what if I want to tag only 1 image and just push a couple of them? i.e. say my local docker stores v.1.1, v.1.2 and v.1.3. I would like to tag v.1.3 with latest and push both v.1.3 and latest but leave older versions where they are?Meredith
This does not work if you have multi-platform images and the original image you are trying to re-tag is not on the local machine (i.e. you first have to pull it). This is because "docker pull" only pulls the image layers that match the local machine's platform architecture, then if you push the new tag(s) you will only push an image with a single platform architecture layer.Really
Maybe it was true at some point but it is no longer true that all tags are pushed by default. You need the -a or --all-tags option. docs.docker.com/engine/reference/commandline/push/#all-tagsGet
Like mentioned in other questions you don't need to pull whole image if you are using tools like buildx imagetools or crane. Please add notice about this to your answerAudrieaudris
P
13

With google's crane you just do

crane tag myregistry.com/myimage:v1.2.3 staging

It works with both docker images and OCI images, no image is downloaded locally and it even skips the layer verifications, as they are guaranteed to already be in the repository.

It's even available in a docker image: gcr.io/go-containerregistry/crane

Note that there are other similar tools, like regctl or skopeo

Pavis answered 19/11, 2022 at 12:9 Comment(0)
V
6

pull/tag/push method will have time&network costs, you can just remotely tag your image with:

only for changing TAG the answer https://mcmap.net/q/373309/-how-to-tag-image-in-docker-registry-v2-duplicate works , but I wanted to change the repository name too.

by many thanks to this, I changed the repoName too!

(by help of his Github project):

1- get manifests (in v2 schema)
2- post every layer.digest in the new repo
3- post config.layer
4- put whole manifest to new repo


details:

1- GET manifest from reg:5000/v2/{oldRepo}/manifests/{oldtag} withaccept header:application/vnd.docker.distribution.manifest.v2+json

2- for every layer : POST reg:5000/v2/{newRepo}/blobs/uploads/?mount={layer.digest}&from={oldRepoNameWithaoutTag}

3- POST reg:5000/v2/{newRepo}/blobs/uploads/?mount={config.digest}&from={oldRepoNameWithaoutTag}

4- PUT reg:5000/v2/{newRepo}/manifests/{newTag} with content-type header:application/vnd.docker.distribution.manifest.v2+json and body from step 1 response

5- enjoy!

Vishinsky answered 20/1, 2020 at 11:9 Comment(4)
I get this error with this approach, any idea? Error: {"errors":[{"code":"MANIFEST_INVALID","message":"manifest invalid","detail":"Corrupt manifest my-app-name: No content to map due to end-of-input\n at [Source: (com.amazonaws.services.s3.model.S3ObjectInputStream); line: 1, column: 0]"}]}Cila
@Cila may you provide your complete request, too?Vishinsky
Sorry Negar. I had to revert back all my changes because this approach didn't work and I don't have the actual request now. Thanks for replying to my comment.Cila
Now that ghcr.io supports GITHUB_TOKEN, you can do the following in GA GHCR_TOKEN=$(echo ${{ secrets.GITHUB_TOKEN }} | base64) curl -H "Authorization: Bearer {GHCR_TOKEN}" https://ghcr.io/v2/USER/IMAGE/tags/list OLD_TAG=test MANIFEST=$(curl -H "Authorization: Bearer ${GHCR_TOKEN}" https://ghcr.io/v2/USER/IMAGE/manifests/$OLD_TAG) CONTENT_TYPE="application/vnd.docker.distribution.manifest.v2+json" NEW_TAG=latest curl -f -X PUT -H "Content-Type: ${CONTENT_TYPE}" -H "Authorization: Bearer ${GHCR_TOKEN}" -d "${MANIFEST}" "https://ghcr.io/v2/USER/IMAGE/manifests/$NEW_TAG" Dolomite
H
4

For single platform images, you can use

docker pull repo:oldtag
docker tag repo:oldtag repo:newtag
docker push repo:newtag

However, there are a few downsides.

  1. You pull all the layers even if you don't need to run the image locally.
  2. You are dereferencing multi-platform images to your local platform.

This can be done with curl, especially if you are in the same repository (when you go across repositories, you also need to copy all the blobs). However, that has two challenges of its own:

  1. You need to accept all of the possible media types for the image you are pulling, track the media type you received, and use that same media type when pushing the manifest back to the registry. There are at least 6 media types I'm familiar with:
  • a signed and unsigned docker schema v1
  • the common manifest and manifest list in docker schema v2
  • the OCI image and index
  1. If you go across repositories, you need to parse the manifest for the included blobs, which can be recursive for docker manifest lists and OCI indexes. You may be able to do a server side blob mount to avoid pulling and pushing the blob, but that will depend on the server support. And we're also going to see anonymous blob mounts come to registries, which allows a blob mount even if you don't know the source repo on that registry (useful for everyone pushing to a cloud registry with an image based on an official docker image from Docker Hub).

  2. Authorization gets complicated, particularly if you have bearer tokens to request and maintain between commands.

The solution I'd recommend is using a tool that handles the registry API for you. I've been working on my own tooling for this, regclient, and there are other similar projects like Google's crane and RedHat's skopeo. These should each handle the media types, copying blobs when needed, and authorization issues that would complicate the curl command.

As an example with regclient's regctl command, you'd run:

regctl image copy repo:oldtag repo:newtag
Hurtle answered 30/12, 2021 at 13:45 Comment(1)
This solution is the only way I found (besides curl) that does not create manifest lists (like docker manifest create or docker buildx imagetools create do. Was using regclient previously anyway - thanks!Rebirth
I
2

There is a simpler method with the new experimental manifest Docker commands. It only requires downloading and uploading the manifest file (JSON overview) of an image. The commands below have been tested with the GitLab registry. First build and push a Docker image in some previous stage:

docker build -t registry.gitlab.com/<group>/<project>/<image-name>:<tag-a> .
docker push registry.gitlab.com/<group>/<project>/<image-name>:<tag-a>

Then at a later stage where the image has not been pulled:

docker manifest create registry.gitlab.com/<group>/<project>/<image-name>:<tag-b> \
    registry.gitlab.com/<group>/<project>/<image-name>:<tag-a>
docker manifest push registry.gitlab.com/<group>/<project>/<image-name>:<tag-b>

You can then pull the image with the new tag. The first step did not seem to work with public Docker Hub images, but any suggestions are welcome. Additionally, to see the manifest itself, run:

docker manifest inspect registry.gitlab.com/<group>/<project>/<image-name>:<tag-a>
Inanity answered 26/11, 2021 at 15:18 Comment(2)
"docker manifest create" only seems to work with OCI images that are not manifest lists them selves (e.g. when the image is a multi-platform image manifest). Probably why it did not work with public Docker Hub images as most of them are multi-platform imagesReally
Furthermore, docker manifest create itself creates manifest lists.Rebirth

© 2022 - 2024 — McMap. All rights reserved.