This article explains the best Docker registry tools for browsing registries/images and manipulating/copying images. It comes with elaborate feature comparison tables. I also explain use cases that illustrate why and when you should use these tools. The analyzed tools include Skopeo, Regctl, ORAS CLI, crane, and many others. Finally, I provide a list of temporary registries you can use as a sandbox for testing these tools.
Introduction
There are many well-known (and little-known) third-party tools that help you work with Docker/container images. This article presents all the tools you need to know. I decided to group them in two categories:
- Registry browser tools, which list images of a registry, list the tags of a specific image, dive deep into the JSON manifests that a tag points to, or even inspect the contents of individual layers
- Image manipulation tools, which copy, manipulate, or delete image tags or manifests. These are all small CLI tools that can do their job without a heavy-weight daemon/server process like the Docker daemon
Why should you care about using such tools? Why is it useful to better understand the internal structure of a container image, or execute advanced operations on them? Here are three reasons:
- It helps you with improving the efficiency of your image layer caching, and therefore improving your image build speed, because:
- Understanding how to analyze images enables you to debug problems with remote caching.
- You will realize that some statements in a
Dockerfile
do not produce an image layer (e.g.CMD
orENTRYPOINT
), thus their order in theDockerfile
does not matter when you try to optimize layer caching. If you are interested in this topic, see this blog series about optimizing Dockerfiles, or this BuildKit-specific post.
- By running a private image registry, you improve the resilience of your deployed system. However, you must know the tools for copying images from Docker Hub (or other registries) to your registry.
- You will improve the overall security of your system by digitally signing images (and verifying existing signatures). These signatures are also uploaded to the image registry, and you need to copy/create them explicitly. Being able to inspect them helps with diagnosing problems. See this article series where I go into signing images in detail.
Registries for (temporary) testing
While using tools of either category, it helps having a temporary image registry at hand to which you can quickly push (or copy) images. After pushing them, you can use the tools presented below to inspect or manipulate them further, all without “messing” with your production registry.
I found the following image registries to be useful:
- Local registries that you can start with “
docker run
”:- CNCF Distribution registry is the reference implementation of the distribution specification. It is based on an older distribution spec version 1.0.1. It does not offer a web interface. See this guide for getting started.
- Zot registry implements the more modern distribution spec version 1.1 and offers a web interface. See here for how to get started with running Zot in Docker.
- Remotely hosted registry: ttl.sh is a free registry that does not require any authentication to push or pull images. It automatically deletes the images after some time. Keep in mind that anyone can access the images you push there!
Note: Pushing images to a local registry (that runs over HTTP, not HTTPS) may require tuning in your image builder or image manipulation tool. These tools expect to work with HTTPS registries by default, often rejecting to communicate with HTTP-only registries, unless you configure them.
Registry browser tools
The following table provides an overview of the available tools.
oci.dag.dev Source, public demo instance | Docker Registry Browser Source | ORAS Artifact explorer Source, public demo instance | Docker Registry UI Source, public demo instance | Registry explorer Source | |
Implementation technology | Web server rendering a web UI | Web server rendering a web UI | Web server rendering a web UI | Web server rendering a web UI | Docker Desktop extension |
Browse multiple registries | ✅ | ❌ | ✅ | ⚠️ (yes, but only for registries you control) | ❌ |
Supports deleting tags | ❌ | ✅ | ❌ | ✅ | ❌ |
List all images of a registry | ✅ | ✅ | ✅ | ✅ | ❌ |
Display raw/detailed image manifest | ✅ | ✅ | ✅ | ❌ | ✅ |
Inspect image layer contents | ✅ | ❌ | ⚠️ (only for plain-text blobs, not for tar.gz layers) | ❌ | ⚠️ (supports tar.gz directory listing, but won’t show contents of files within tar.gz) |
Supports local HTTP-only registry | ✅ | ✅ | ❌ Requires ngrok-like workaround | ✅ | ❌ |
Supports registry that requires authentication | ✅ | ✅ | ❌ | ✅ | ❌ |
Browsing local HTTP-only registries
Some tools presented in the above table, e.g., ORAS Artifact explorer, do not support accessing local HTTP-only registries. They only support HTTPS-based registries with a valid SSL certificate. To work around this limitation, you can use a service such as Ngrok to create a temporary SSL tunnel to your registry. For instance, after you set up the Ngrok CLI and started the CNCF Distribution registry (that I mentioned above) on its default port 5000, just run
“ngrok http 5000
” to create the tunnel to your local registry.
Let’s look at each solution in detail in the tabbed box below:
Registry manipulation tools
The following table lists various use cases (that manipulate or create images) that I found relevant in the past years, see the left column, and then elaborates which tool implements this use case:
Crane | ORAS | Skopeo | Regctl | |
Copy image/manifest | ✅ | ✅ Incl. referrers | ✅ – Incl. Cosign signatures (but not referrers) – Can (re-) sign destination image with embedded Cosign – Can validate existing Cosign signatures of source image (using a custom trust policy) | ✅ Incl. referrers or Cosign signatures |
Synchronize images | ⚠️ All-or-nothing approach, via “ copy --all-tags ” | ❌ | ✅ All tags, or only specific ones (semantic version comparison or regex) | ✅ Via separate tools: regsync syncs all images/repositories, or specific tags via regex. regbot offers Lua scripting for custom use cases. |
Delete image/manifest | ✅ | ✅ | ✅ | ✅ |
Delete blob | ❌ | ✅ | ❌ | ✅ |
Create/build new manifest | ✅ | ❌ | ❌ | ✅ |
Re-tag manifest | ✅ | ✅ | ✅ via “copy” cmd | ✅ |
Upload local files(s) / folder(s) as image/artifact | ✅ | ✅ | ❌ | ✅ (only individual files, not folders) |
Upload and attach artifact for existing manifest | ❌ | ✅ | ❌ | ✅ |
Usable as library | ✅ (Golang) | ✅ (Golang, Java, Rust, Python) | ⚠️ (not directly, see here) | ✅ (regclient Golang library) |
Here is an explanation of each use case. What is it and when does it play a role?
- Copy image: Copying a few/specific tags (or digests) of an image (or other OCI artifacts) from one registry to another. For instance, you might not trust (or cannot access) Internet registries from your deployed environment, so instead you only trust private registries. A concrete example could be Docker Hub, which is affected by image pull limits, and it could cause downtime in your production environment if you were to pull images from Docker Hub directly during a deployment. So instead, you copy images from Docker Hub to a private registry and deploy the images from there.
- Synchronize images: copying many (or all) tags/manifests of one (or more) images from a source registry to a destination registry, e.g. because you want to mirror the source registry. An example scenario is that you want to limit registry access in your deployment to your destination registry, e.g. an “air-gapped deployment”.
- Delete image/manifest or delete blobs: deleting tags or manifests, typically to reclaim storage space in a private registry. See the “Caveats of deleting manifests via tag or digest” box below for more details and caveats.
- Create/build new manifest: suppose the image builder tools that you or someone else uses created an image that does not perfectly fit your needs, yet. Instead of rebuilding the image from scratch, you want to manipulate the existing one, because it is faster. Think of this like a “video post production” fix for an image. Examples:
- Merging separately-pushed platform images (that have one separate tag per platform) to a single multi-platform image tag
- Changing how the image behaves, e.g. by manipulating the
ENTRYPOINT
orCMD
of an image, or its environment variables - Change annotations of the image manifest, or delete sensitive build arguments whose values you don’t want to be present in the image manifest
- Adding or removing files to the image
- Re-tag manifest: adding (yet another) tag to an already-uploaded image manifest, without wasting time downloading the entire image and its layers (which would happen if you used “
docker pull … && docker tag <old> <new> && docker push <new>
”). Example: an environment promotion scenario, where a Continuous Deployment pipeline adds tags such as “staging” or “production” to an existing image already tagged with “development”, and then deploys that new tag. - Upload local files(s) / folder(s) as image/artifact: there are scenarios where you need to access various (large) files in your deployed environment, which you don’t want to include into your application (code) image, as it would become too large, or because you wouldn’t be able to change the files independently from the application code. Examples are machine learning models, database dumps, tool binaries, website assets, etc. Instead, you upload these files/folders as OCI artifacts to the image registry, treating the image registry as arbitrary blob storage (basically an alternative to S3 storage).
- Upload and attach artifact for existing manifest: similar to the above use case (“Upload local files(s) / folder(s) as image/artifact”), but this uses the registry’s references/referrers feature that allows a registry client to look up associated artifacts for an image (see the Notation article for details). E.g. uploading an SBOM (or an image signature) for a specific image. See this article series to learn more about signing and attesting images.
- Usable as library: in cases when the existing commands of the CLI tool don’t behave the way you need them to, or some feature is missing, you will need to write custom tooling (in some language), using a library (SDK) to interact with image manifests. These are edge cases, but they do exist. Think of it as replacing complex Bash scripts (that make various calls to the above CLI tools) with a more maintainable Go program that can do better error handling and input/output validation.
- Example 1: want to regularly delete unused images from your registry, but the existing commands only support the push/pull date as selection criterion. However, you also need to consider the image size because you only want to delete images larger than 1 GB.
- Example 2: after synchronizing an off-the-shelf image from Docker Hub, you want to ensure that the image in your registry does not contain certain binaries such as a
/bin/sh
orcurl
, to improve the security of your image. You could implement a custom tool that scans the layers of the synchronized image and if it finds such binaries, it adds another layer that removes them.
Here are a few more details about each tool:
Why copying Cosign signatures across registries does work
When you copy an image from one registry to another and the source registry stores a Cosign signature of that image, then you can (and should) copy that Cosign signature to the destination registry along with the image. This allows you to verify the image when deploying it from the destination registry.
At first, I thought that copying Cosign signatures should not work. Why? Because the signature’s subject clearly indicates the full image name (including the source registry hostname) and its digest, as the following example demonstrates (which is the content of a application/vnd.dev.cosign.simplesigning.v1+json
manifest):
{
"critical": {
"identity": {
"docker-reference": "ttl.sh/signtestt" // full image name of the source registry
},
"image": {
"docker-manifest-digest": "sha256:1c4eef651f65e2f7daee7ee785882ac164b02b78fb74503052a26dc061c90474"
},
"type": "cosign container image signature"
},
"optional": null
}
Code language: JSON / JSON with Comments (json)
However, Cosign only verifies that the digest of the image you are verifying matches the value of docker-manifest-digest
of the above manifest. Cosign’s verification process ignores the docker-reference
value, presumably to make copying image signatures possible.
Conclusion
I presented many tools, but you likely won’t have time to try each one. Hence, this is my personal recommendation:
- For image browsing, I only use (and highly recommend) oci.dag.dev, because it offers the most features
- For manipulating images, it depends on the use case. Skopeo has the advantage of being most feature-complete when it comes to copying/syncing images that involve Cosign signatures, as it allows checking signatures or signing unsigned images.
Did I miss any tool, or do you want to share your experience? Let me know in the comments!