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.
- You pull all the layers even if you don't need to run the image locally.
- 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:
- 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
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).
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