Skip to content
qibdo qibdo
Theme
Book a demo

Push your first image

This guide pushes a local image to the registry with a standard container client, then checks that what you pushed is what the registry holds. The registry speaks the push half of the OCI Distribution API, so docker push, podman push, crane push, and skopeo copy all work.

  • Authentication. Export an API token as QIBDO_API_TOKEN. See Authenticate a user and manage the session.
  • Permissions. An IAM identity whose role or access policy conveys push on the repository you are going to write to. To create the repository by pushing to it, the identity also needs registry.repository.create in the workspace. See the authorization model.
  • An active workspace. The organisation and the workspace must already exist. A push never creates either of them. A suspended or archived workspace refuses pushes, even though a suspended workspace still serves pulls.
  • A local image to push, and your deployment’s registry hostname:
Terminal window
export QIBDO_API_TOKEN="<your-token>"
export REGISTRY_HOST="registry.qibdo.example.com"

Your username is your account email, and your password is an access token, not your console password.

Terminal window
docker login "$REGISTRY_HOST" -u "you@example.com" --password-stdin <<< "$QIBDO_API_TOKEN"

Authenticating to the registry covers the exchange and what each token is scoped to.

The reference is the registry host, then the organisation, the workspace, and the repository. Both the organisation and the workspace accept either its name or its UUID:

Terminal window
docker tag checkout-api:local "$REGISTRY_HOST/acme/production/checkout-api:v1.4.0"
Terminal window
docker push "$REGISTRY_HOST/acme/production/checkout-api:v1.4.0"

If the repository checkout-api does not exist yet, the push creates it when the first blob is committed. The repository can therefore appear before the image manifest is published. If a push stops after committing a blob, the repository and blob can remain even though the image cannot yet be pulled.

Pull the image back and compare the digest with the one your client printed after the push:

Terminal window
docker pull "$REGISTRY_HOST/acme/production/checkout-api:v1.4.0"
docker image inspect "$REGISTRY_HOST/acme/production/checkout-api:v1.4.0" \
--format '{{.RepoDigests}}'

The registry computes each digest from the bytes it received, and refuses content that does not match the digest you declared. The digest is the same on every registry that holds the image.

A push has two parts. The client uploads each layer and the configuration as a blob, then publishes a manifest that names them.

  • Small blobs go in a single request. Larger blobs open an upload session, send their bytes in chunks, and commit with the digest.
  • The registry advertises a minimum chunk size of 5 MiB in the OCI-Chunk-Min-Length header when a session opens. Standard clients honour it. A chunk smaller than the minimum is still accepted, and the registry keeps it safely so the upload continues.
  • A manifest is accepted only if everything it names exists in the repository you push to. A manifest that names a missing layer, or an index that names a missing child manifest, is refused.
  • Publishing a tag creates or moves it. Publishing by digest changes no tag. If two clients move the same tag at once, one of them wins. Both receive success, and the tag never names a manifest that does not exist.
  • Pushing the same content twice is harmless. Two clients that push the same blob at the same moment both succeed.

An upload session lives in the registry’s database and object store, not in one application instance. If your connection drops, or the instance that served your last chunk goes away, ask the registry where the upload stands and continue from there. The registry answers with the range it holds. A chunk that does not continue from that position is refused, so a duplicate or a gap cannot corrupt the blob. Clients that follow the specification resume without your help. A session that nobody finishes expires.

A push can ask the registry to reuse a layer that another repository in the same workspace already holds, so the bytes are not sent again. The registry honours the request when your token also permits pulling from the source repository. Every case it does not honour falls back to a normal upload, and the response does not tell you whether the source exists.

LimitValueWhy
Manifest size4 MiBThe same ceiling as on the pull path. A manifest above it is refused with SIZE_INVALID.
Blob sizeabout 48.8 GiB (50,000 MiB) at the default 5 MiB part sizeThe object store accepts at most 10,000 parts. The ceiling depends on the deployment’s configured part size; an oversized blob is refused with SIZE_INVALID.
Uploads in progress per workspace16 by defaultA quota. It bounds what one workspace can hold open at once, and an administrator can override it for a workspace.

The registry does not hold a blob in memory. Memory per upload depends on the part size, not on the blob size. The memory available to all uploads together has a fixed budget. When pushes would exceed it, the registry answers 429 TOOMANYREQUESTS with a Retry-After header, and standard clients wait and retry. Only pushing slows down. Pulls and other services are not affected.

What you seeWhat it means
UNAUTHORIZEDNo credential, or one the registry could not verify. Run docker login.
DENIEDYour token names this repository but does not include push, or the workspace is suspended or archived. It is also returned when you try to remove content that a deployment lock pins.
NAME_UNKNOWNThe repository does not exist and you may not create it, or your credential does not cover it. These are deliberately indistinguishable. See Pulling images.
DIGEST_INVALIDThe content you sent does not match the digest you declared. The blob is refused and nothing of it becomes reachable.
BLOB_UPLOAD_UNKNOWNThe upload session does not exist, has expired, or is not available to you. Start the upload again.
MANIFEST_BLOB_UNKNOWNThe manifest names a layer, a configuration, or a child manifest that the repository does not hold. Push the missing content first.
SIZE_INVALIDThe manifest or the blob is larger than the limit above.
TOOMANYREQUESTSThe registry is at its upload budget. Wait for the time in Retry-After, then try again.

You can remove a manifest by its digest, which removes the manifest and every tag that names it. You can also remove a single tag, or a repository’s reference to a blob. A blob that another repository still references stays reachable through that repository.

Removing content does not free storage space yet. The registry stops serving what you removed, and the bytes are reclaimed by a later process. Content that a deployment lock pins cannot be removed.