Cloud Container Registry & Image Management

Manage container images securely. Covers registry selection, image scanning, signing, layer caching, multi-arch builds, and image lifecycle policies.

Cloud Container Registry & Image Management is the operational foundation for distributing and maintaining container images across development, staging, and production environments. It matters when teams build, test, and deploy containerized applications at scale—especially when multiple teams or services rely on shared base images, versioned artifacts, and automated pipelines that push images to a central store.

Image Lifecycle: Build, Push, and Tag

The core workflow begins with building a container image and publishing it to a registry. A typical build step uses docker build with exact flags to ensure reproducibility and consistency.

docker build \
  --tag myapp:1.2.3 \
  --build-arg ENV=prod \
  --build-arg GIT_COMMIT=abc123def \
  --cache-from myapp:latest \
  --progress=plain \
  -f Dockerfile.prod \
  .

This command produces a single image with metadata embedded in the image layers. The --tag flag specifies the image name and version, while --build-arg injects build-time variables. The --cache-from flag reuses previous image layers, reducing build time when the Dockerfile or dependencies change.

The image is then pushed to a remote registry using docker push, which performs a multi-stage upload with layer streaming, integrity checks, and resumable transfers.

docker push myapp:1.2.3

Under the hood, docker push uses the OCI distribution specification: it sends image layers, manifests, and configuration files over HTTP. Each layer is uploaded as a blob, verified with a SHA-256 digest, and registered in the registry’s index. The registry responds with a 201 Created status and a Docker-Content-Digest header, which contains the final digest of the image manifest.

When pushing fails, the most common error is failed to push image: unexpected EOF during layer upload. This often indicates network instability or a registry that requires Content-Length headers but does not accept Transfer-Encoding: chunked. To resolve, ensure the registry server configures chunked upload handling and that the client sends Content-Length explicitly.

Image Versioning and Tagging Strategies

Image versioning relies on tags—strings that identify a specific image version. Tags can be simple versions (1.2.3), environment-specific (prod, staging), or commit-based (abc123def). The registry treats tags as references to image manifests.

To manage multiple tags efficiently, use docker tag to create aliases.

docker tag myapp:1.2.3 myapp:latest
docker tag myapp:1.2.3 myapp:prod
docker tag myapp:1.2.3 myapp:1.2

When a new image is pushed, it is common to update multiple tags. The registry must support atomic tag updates, where a tag update is either fully applied or rolled back. A failure in this process—such as a partial tag update—can result in a tag pointing to an incomplete or inconsistent image.

Tagging strategies emerge from this foundation:

A common failure mode is tag drift: a latest tag that points to an image built yesterday, while prod points to one from last week. To prevent this, pipelines should include a step that explicitly updates the latest tag after every successful build.

Image Discovery and Retrieval

Once images are published, they must be discoverable and retrievable. The primary interface is a registry API, typically a RESTful service exposing image metadata and manifest information.

To inspect an image remotely:

curl -H "Authorization: Bearer $TOKEN" \
     "https://registry.example.com/v2/myapp/manifests/1.2.3"

The response includes the image’s mediaType, schemaVersion, config, and layers. The config field points to a JSON blob containing metadata such as Cmd, Env, WorkingDir, and Entrypoint.

To retrieve an image layer:

curl -H "Authorization: Bearer $TOKEN" \
     "https://registry.example.com/v2/myapp/blobs/sha256:abc123def456"

The registry returns a 200 OK with the layer data, and the Content-Digest header includes the SHA-256 of the layer. This digest is critical for verifying image integrity during deployment.

A frequent failure is image pull timeout. If the registry is slow or the network is high-latency, docker pull may time out during layer downloads. The error failed to pull image: context deadline exceeded appears when the puller waits longer than the configured timeout. To mitigate, increase the --timeout in docker pull, or use docker pull --pull-all to fetch all layers in parallel.

Image Metadata and Annotations

Metadata is stored in the image manifest and config. The manifest schema version 2 supports annotations—key-value pairs that describe the image beyond the basic metadata.

Example manifest with annotations:

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "digest": "sha256:abc123def456",
    "size": 2048
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar",
      "digest": "sha256:123456abcdef",
      "size": 10240
    }
  ],
  "annotations": {
    "org.opencontainers.image.created": "2024-05-20T10:00:00Z",
    "org.opencontainers.image.title": "My Application",
    "org.opencontainers.image.description": "A microservice for user management",
    "org.opencontainers.image.version": "1.2.3",
    "org.opencontainers.image.authors": "[email protected]",
    "org.opencontainers.image.url": "https://github.com/example/myapp"
  }
}

Annotations are critical for tooling: CI/CD systems use them to determine which image to deploy, and orchestrators use them to configure services. A missing or misformatted annotation can cause a deployment to fail silently.

A common mistake is assuming that annotations are stored in the image config instead of the manifest. This leads to tools that read config.annotations but find nothing. To avoid this, always verify that the annotations are at the top level of the manifest, not nested under config.

Image Lifecycle Management: Pruning and Cleanup

Over time, registries accumulate images. Unused tags, redundant layers, and outdated versions consume storage and degrade performance.

To clean up, run a periodic pruning job that removes images not referenced by any tag.


curl -H "Authorization: Bearer $TOKEN" \
     "https://registry.example.com/v2/myapp/tags/list" \
  | jq -r '.tags[] | . as $tag | $tag | "myapp:\($tag)"'

Then, for each image, check if it is referenced by any tag:

# Check if image digest is referenced by any tag
curl -H "Authorization: Bearer $TOKEN" \
     "https://registry.example.com/v2/myapp/manifests/sha256:abc123def456" \
  | jq -r '.layers[].digest'

A digest is unused if it does not appear in the manifest of any tag.

The pruning job can be automated using a script that:

  1. Fetches all tags
  2. Collects all layer digests used by those tags
  3. Compares with a list of all layer digests in the registry
  4. Deletes unused layers and manifests
# Delete unused layer
curl -X DELETE \
     -H "Authorization: Bearer $TOKEN" \
     "https://registry.example.com/v2/myapp/blobs/sha256:123456abcdef"

A common failure is deleting a layer that is still referenced by a tag. This happens when the pruning job runs during a concurrent build. To prevent this, use a locking mechanism: acquire a lease on the registry during pruning, or use ETag-based conditional deletion.

Image Security and Trust

Images must be trusted. A registry supports signing, where a digital signature is attached to an image manifest.

To sign an image:

cosign sign \
  --key myapp.key \
  --certificate myapp.crt \
  --message "Deployed to prod" \
  myapp:1.2.3

The signature is stored as a cosign artifact in the registry under a standard path:

myapp/manifests/sha256:abc123def456/cosign/1.2.3

The signature includes the image digest, the signer’s identity, and a timestamp.

To verify an image:

cosign verify \
  --certificate myapp.crt \
  myapp:1.2.3

The verifier checks the signature’s cryptographic integrity and validates that the image digest matches the manifest.

A frequent issue is missing or expired certificates. The error error: x509: certificate has expired appears when the verifier cannot validate the signer’s certificate. To prevent this, use long-lived certificates and automate certificate rotation.

Another failure is signature verification failing due to a mismatch between the config.digest and manifest.digest. This occurs when the image config is updated but the manifest is not re-signed. The solution is to ensure that the signing process includes both the manifest and config digests in the signature.

Image Deployment and Rollback

Deployment depends on image reference. A service definition typically specifies an image tag.

Example Kubernetes deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: app
          image: myapp:1.2.3

When the deployment is applied, kubectl pulls myapp:1.2.3 from the registry and deploys it.

A common pattern is zero-downtime rollout: the new version is deployed alongside the old, and traffic is shifted gradually.

To rollback, simply update the image field to point to a previous version.

# Rollback to version 1.2.2
kubectl patch deployment myapp \
  -p '{"spec":{"template":{"spec":{"containers":[{"name":"app","image":"myapp:1.2.2"}]}}}}'

The registry must support image versioning and tag consistency. A failure occurs when the myapp:1.2.2 tag points to a partial or corrupted image. The deployment may start, but the pod fails to start due to missing files or incorrect entrypoint.

To detect such failures, use kubectl rollout status and kubectl describe pod to inspect the image pull logs:

kubectl describe pod myapp-abc123

This reveals whether the image was pulled successfully, and if so, which digest was used.

Image Registry Health and Monitoring

A registry’s health is critical. The primary metrics are:

Monitor these using a simple health check endpoint:

curl -I https://registry.example.com/health

A healthy registry responds with 200 OK and includes the current storage usage in the body:

{
  "status": "healthy",
  "storage": {
    "total": 53248000000,
    "used": 34182500000,
    "free": 19065500000
  },
  "last_updated": "2024-05-20T10:05:00Z"
}

A failure appears as 503 Service Unavailable with a body indicating the cause:

{
  "status": "unhealthy",
  "error": "disk full",
  "details": {
    "disk_usage": 97,
    "threshold": 90
  }
}

When the registry becomes unavailable, deployments stall, and new pods fail to start. The error failed to pull image: rpc error: code = Unknown desc = failed to pull image: dial tcp: i/o timeout indicates network or registry unavailability.

To ensure reliability, maintain a registry cluster with load balancing and persistent storage. Use a health check script to detect and alert on failures.

This page was rewritten on 16 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.