Tutorial  on  Containers

How Container Registries Work: Pushing and Pulling Images By Hand

Container registries look simple until you need to debug what was actually pushed, why a pull picked the wrong image, or why deleting a tag didn't remove anything. Learn how registries work by pushing, pulling, inspecting, and deleting image data directly through the Registry API.

Premium Tutorial

Upgrade your membership to unlock this and all other premium materials.

Upgrade

Most of the time, we interact with container registries via the docker pull and docker push commands or by setting an image name in a Kubernetes manifest. However, sooner or later you'll run into cases where you need to talk to the registry directly: a tag points to the wrong image manifest, a pull fails because the client requested the wrong platform, a layer is missing, or an accidentally pushed image needs to be removed for real (not just untagged). This is where understanding how registries actually work comes in handy.

In this tutorial, you'll practice working with the Registry API directly. You'll upload and download raw blobs, assemble and push an image by hand, list tags, pull image contents without using Docker, see why deleting images is trickier than it sounds, and finally inspect how multi-platform images are represented in a registry.

Sounds a bit too low-level? Fear not, the Registry API is actually quite simple and easy to use with plain curl.

Registry API Demystified

Most if not all modern container registries implement the OCI Distribution Specification, which "defines an API protocol to facilitate and standardize the distribution of content". Despite the fancy sound of the previous sentence, the Registry API is actually concise and easy to understand, especially if you approach it from the right angle. Below is an overview of the key container registry operations - traditionally augmented with hands-on examples.

Container Registry: Connecting the dots between the key API endpoints and the underlying data flow.

Uploading and Downloading Blobs

At its core, a registry is a content-addressable blob store. This means you can upload any blob (i.e., an arbitrary file) into a registry by hashing the file locally and using its digest as the future address:

POST /v2/<repo>/blobs/uploads/ -> Location: <url> (response header)
PUT <url>?digest=<digest>

The above monolithic PUT request is the simplest way to upload a blob but it's definitely not the most efficient one. For larger blobs, the OCI Distribution Spec offers a lengthier yet more efficient alternative POST + PATCH + PUT approach to upload files in chunks. But since all blobs in this tutorial are intentionally small, we'll stick with the "monolithic PUT" approach for the sake of brevity.

Hands-On: Upload a blob to a registry using curl

A sample blob.tar.gz is already waiting for you in the home directory ~ on host-1. Compute its digest and push it to a fresh hello-world repository in the playground's registry.iximiuz.com registry:

host-1
BLOB=~/blob.tar.gz
DIGEST=$(sha256sum ${BLOB} | awk '{print $1}')
  1. Initialize an upload session and capture the location URL from the response header:
host-1
LOCATION=$(curl -s -o /dev/null -X POST \
    -w '%header{location}' \
    "https://registry.iximiuz.com/v2/hello-world/blobs/uploads/")
  1. Send the blob bytes together with the digest to the returned location URL:
host-1
curl -s -i -X PUT \
    -H "Content-Type: application/octet-stream" \
    --data-binary @"${BLOB}" \
    "${LOCATION}&digest=sha256:${DIGEST}"

A successful upload responds with HTTP/2 201 Created and a Location header pointing to the new blob.

Downloading a blob from a registry is even simpler. Assuming you know the blob's digest, you can simply GET it from the registry:

GET /v2/<repo>/blobs/<digest>
Hands-On: Download a blob from a registry using curl

First, note the digest of the blob you just uploaded from host-1:

host-1
echo ${DIGEST}

Then switch to the host-2 terminal, and using the digest from above, download the blob back from the registry:

host-2
DIGEST=<paste the digest from host-1>

curl -s -L -o /tmp/blob.tar.gz \
    "https://registry.iximiuz.com/v2/hello-world/blobs/sha256:${DIGEST}"

Verify the blob "round-tripped" intact: the locally computed digest must match the one we uploaded with from host-1:

host-2
sha256sum /tmp/blob.tar.gz

Premium Materials

Official Content Pack required

This platform is funded entirely by the community. Please consider supporting iximiuz Labs by upgrading your membership to unlock access to this and all other learning materials in the Official Collection.

Support Development