---
url: /en/artifact/docker.md
description: >
  Guide for logging in, pushing, and pulling Docker container images from the
  CNB Docker Artifact Registry, including artifact path naming rules and CI
  pipeline usage.
---
## Login to CNB Docker Artifact Registry

You can use CNB access token as login credentials.
For how to get `CNB_TOKEN`, please refer to \[Creating an Access Token]
(./intro.md#creating-an-access-token). Login command:

```bash
docker login docker.cnbprod.alpha.qq.com -u cnb -p <YOUR_TOKEN>
```

## Docker Artifact Path Rules

When publishing artifacts to a repository, two naming rules are supported:

1. **Same-name artifacts**: Artifact path matches repository path, e.g.:
   `docker.cnbprod.alpha.qq.com/${CNB_REPO_SLUG_LOWERCASE}`
2. **Different-name artifacts**: Repository path serves as namespace,
   artifact path = repository path/artifact name, e.g.:
   `docker.cnbprod.alpha.qq.com/${CNB_REPO_SLUG_LOWERCASE}/<IMAGE_NAME>`

## Pushing Artifacts

### Push from Local Command Line

Same-name artifacts

```bash
docker build -t docker.cnbprod.alpha.qq.com/${CNB_REPO_SLUG_LOWERCASE}:latest .
docker push docker.cnbprod.alpha.qq.com/${CNB_REPO_SLUG_LOWERCASE}:latest
```

Different-name artifacts

```bash
docker build -t docker.cnbprod.alpha.qq.com/${CNB_REPO_SLUG_LOWERCASE}/<IMAGE_NAME>:latest .
docker push docker.cnbprod.alpha.qq.com/${CNB_REPO_SLUG_LOWERCASE}/<IMAGE_NAME>:latest
```

### Push in Cloud Native Build

```yaml title=".cnb.yml"
main:
  push:
    - services:
        - docker
      stages:
        - name: docker build
          script: docker build -t ${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}:latest .
        - name: docker push
          script: docker push ${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}:latest
```

### Push in Workspaces

Same-name artifacts

```bash
docker build -t ${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}:latest .
docker push ${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}:latest
```

Different-name artifacts

```bash
docker build -t ${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}/<IMAGE_NAME>:latest .
docker push ${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}/<IMAGE_NAME>:latest
```

## Using Artifacts

### Use in Command Line

```bash
docker pull docker.cnbprod.alpha.qq.com/<ARTIFACT_PATH>:latest
```

### Customize Build/Dev Environment

Reference a Docker image from the CNB artifact registry as your build environment in `.cnb.yml`:

```yaml{4} title=".cnb.yml"
main:
  push:
    - docker:
        image: ${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}:latest
      stages:
        - name: hello world
          script: echo "Hello World"
```

Or as your cloud native dev environment:

```yaml{4} title=".cnb.yml"
$:
  vscode:
    - docker:
        image: ${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}:latest
      services:
        - vscode
        - docker
```

## Pushing buildx Build Cache

When using `docker buildx build --cache-to type=registry` to push build cache to the registry,
if the cache export fails with **404 Not Found**, it is usually caused by the cache export format
of older buildx/buildkit versions:

```text
#39 writing cache manifest sha256:... done
#39 ERROR: error writing manifest blob: failed commit on ref "sha256:...":
unexpected status from PUT request to https://docker.cnbprod.alpha.qq.com/v2/<REPO>/manifests/<CACHE_TAG>: 404 Not Found
```

In this case the image itself is pushed successfully and only the cache export fails.
This is typical with buildx bundled in older Docker versions such as Docker 27.5.x.

### Solution

Append `image-manifest=true,oci-mediatypes=true` to `--cache-to`:

```bash
docker buildx build \
  --cache-from type=registry,ref=${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}:<CACHE_TAG> \
  --cache-to type=registry,ref=${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}:<CACHE_TAG>,mode=max,image-manifest=true,oci-mediatypes=true \
  -t ${CNB_DOCKER_REGISTRY}/${CNB_REPO_SLUG_LOWERCASE}:latest \
  --push .
```

Alternatively, upgrade Docker/buildx to a version with BuildKit v0.21+
(where `image-manifest` is enabled by default).

See the [official documentation](https://github.com/moby/buildkit#registry-push-image-and-cache-separately)
for these parameters.

### Cause

Older buildx (before BuildKit v0.21) exports multi-arch cache (e.g. `mode=max` with multiple
`--platform`) as an OCI index (manifest list) by default, and the child cache manifests referenced
by the index are uploaded only as plain blobs, never pushed via the manifests API.
When registering an index, the registry requires every referenced child manifest to be
registered already, and returns 404 when no such record is found
(image pushes are unaffected, as their child manifests are always pushed before the index).
`image-manifest=true` changes the export to a **single OCI image manifest**,
a standard format supported by the registry. This parameter requires `oci-mediatypes=true`
(which has defaulted to `true` since BuildKit v0.8 and is written explicitly to ensure it takes effect).
Since BuildKit **v0.21, `image-manifest` defaults to `true`**, and newer versions no longer have this issue.

## Limitations

* The Docker registry only accepts container images (single/multi-arch) and buildx registry cache.
  Pushing other artifact types such as Helm Charts or Docker Model artifacts
  to the Docker registry address will be rejected. Ensure the push target matches the artifact type
  (see [Artifact Type Admission Restrictions](./intro.md#artifact-type-admission-restrictions)).
* Maximum quota per layer: 64 GB
* Maximum number of layers per image: 64
* Maximum size of artifact metadata: 64 KB
* Docker Registry V1 API is not supported. Please use Docker 20.10+ client.

  If you encounter the following errors, please upgrade Docker version:

  ```text
  Error: image <IMAGE_NAME> not found
  ```

  ```text
  Failed to pull image: rpc error: code = Unknown desc = missing signature key
  ```

  If you must use an older version of Docker (below 17.12), you need to manually disable the V1 protocol:

  * Disable V1 via startup parameter: `dockerd --disable-legacy-registry` (Linux platform, default `false`)
  * Disable V1 via config file: set `"disable-legacy-registry": true` in `daemon.json` (All platforms, default Not set)

  > Note: Windows/macOS platforms automatically enforce V2 protocol,
  > no additional configuration needed. This option was removed in Docker 17.12+
  > as V1 support was completely dropped.

## More Information

For more Docker usage, please refer to the [Docker official documentation](https://docs.docker.com/).
