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.
Before you begin
Section titled “Before you begin”- 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
pushon the repository you are going to write to. To create the repository by pushing to it, the identity also needsregistry.repository.createin 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:
export QIBDO_API_TOKEN="<your-token>"export REGISTRY_HOST="registry.qibdo.example.com"Step 1: Log in to the registry host
Section titled “Step 1: Log in to the registry host”Your username is your account email, and your password is an access token, not your console password.
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.
Step 2: Name the image for the registry
Section titled “Step 2: Name the image for the registry”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:
docker tag checkout-api:local "$REGISTRY_HOST/acme/production/checkout-api:v1.4.0"Step 3: Push
Section titled “Step 3: Push”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.
Step 4: Confirm what the registry holds
Section titled “Step 4: Confirm what the registry holds”Pull the image back and compare the digest with the one your client printed after the push:
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.
How a push works
Section titled “How a push works”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-Lengthheader 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.
Resuming an interrupted upload
Section titled “Resuming an interrupted upload”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.
Reusing a layer from another repository
Section titled “Reusing a layer from another repository”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.
Limits
Section titled “Limits”| Limit | Value | Why |
|---|---|---|
| Manifest size | 4 MiB | The same ceiling as on the pull path. A manifest above it is refused with SIZE_INVALID. |
| Blob size | about 48.8 GiB (50,000 MiB) at the default 5 MiB part size | The 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 workspace | 16 by default | A 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.
When a push fails
Section titled “When a push fails”| What you see | What it means |
|---|---|
UNAUTHORIZED | No credential, or one the registry could not verify. Run docker login. |
DENIED | Your 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_UNKNOWN | The 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_INVALID | The content you sent does not match the digest you declared. The blob is refused and nothing of it becomes reachable. |
BLOB_UPLOAD_UNKNOWN | The upload session does not exist, has expired, or is not available to you. Start the upload again. |
MANIFEST_BLOB_UNKNOWN | The manifest names a layer, a configuration, or a child manifest that the repository does not hold. Push the missing content first. |
SIZE_INVALID | The manifest or the blob is larger than the limit above. |
TOOMANYREQUESTS | The registry is at its upload budget. Wait for the time in Retry-After, then try again. |
Removing content
Section titled “Removing content”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.
What to read next
Section titled “What to read next”- Pulling images: reference forms, limits, and what each error code means on the pull path.
- Authenticating to the registry:
how a token comes to convey
push, and what suspension does to it. - Repositories, artifacts, and tags: the resource model behind a push.