The platform embeds an OCI container registry (Docker Registry HTTP API V2) in the app. It opens at /v2 on the same address as the app, so docker · containerd · podman push and pull without a separate registry server. Training runtime images, serving images and imported partner images all live here.

Issue a token

  1. Go to the avatar at the top right → Account settings (계정 설정) → Manage container tokens (컨테이너 토큰 관리). Every member can issue one.
  2. Set a name and the expiry in days (90 by default; for no expiry choose No expiry (무기한)), then press Issue (발급). ① The token is shown only this once, ② together with a docker login command filled in with this server's host and user name.
    Right after issuing a container token — ① the secret is shown only now ② a ready docker login command
Rule
pull (GET · HEAD)The issuer has VIEW or higher in that tenant
push (every other request)The issuer is DEVELOPER · OPERATOR · ADMIN in that tenant
TenantThe tenant at issue time is fixed in the token. The X-Tenant header is not used

Permissions are checked again on every request. A VIEWER can also get a token, but that token can only pull. If the issuer's role changes later, the change applies immediately to tokens already issued. An ADMIN sees every token of the tenant in the list, other members only their own; a token is revoked by its issuer or an ADMIN.

The API issues tokens too.

curl -sS -X POST "$API/api/v1/registry/tokens" \
  -H "Authorization: Bearer $JWT" -H "X-Tenant: DEMO" \
  -H 'Content-Type: application/json' -d '{"name": "ci-push", "expires_in_days": 7}'
# -> {"id": "…", "token": "<secret>", "tenant": "DEMO", "username": "demo", "registry_host": "mlops.example.com"}

Login · push · pull

echo "<issued token>" | docker login mlops.example.com -u demo --password-stdin

docker tag example/mnist-trainer:v1 mlops.example.com/demo/example/mnist-trainer:v1
docker push mlops.example.com/demo/example/mnist-trainer:v1
docker pull mlops.example.com/demo/example/mnist-trainer:v1
  • Any user name works. The token is the password. For readability, the tenant code (lower case) is recommended.
  • Do not put a scheme such as http:// in an image address. It is always host[:port]/path:tag.
  • Storage is fully separated per tenant. The first path segment is just a name; whatever name you push under, the image goes into the token's tenant space. By convention, putting the tenant code in the first segment (demo/…) gives the same shape as images imported on screen.
  • Another tenant's images look as if they do not exist (not found). The registry deliberately answers 404 so that "exists but no permission" cannot be told apart from "does not exist".

The standard API lists repositories and tags.

curl -u "demo:<token>" https://mlops.example.com/v2/_catalog
curl -u "demo:<token>" https://mlops.example.com/v2/demo/example/mnist-trainer/tags/list
# {"name":"demo/example/mnist-trainer","tags":["v1"]}

_catalog lists only your own tenant's repositories. Shared mlops/… images granted to you are not listed, but they pull if you know the tag.

Delete images

/v2 has no delete request, so docker cannot delete images. Delete them in the container registry screen, or call the REST API with the DevelopDEVELOPER · OPERATOR · ADMIN permission.

curl -sS -X DELETE -H "Authorization: Bearer $JWT" -H "X-Tenant: DEMO" \
  "$API/api/v1/registry/repositories/demo/example/mnist-trainer/tags/v1"
# {"name":"demo/example/mnist-trainer","tag":"v1","repository_deleted":true}
  • Deleting the last tag removes the repository too. To delete a whole repository, use DELETE /api/v1/registry/repositories/{name}.
  • If the platform uses the image (a training runtime variant, a model image, a deployment, serving and so on), the answer is 409, with the users listed in detail.references.
  • Shared mlops/… images cannot be deleted.

A registry open over HTTP (insecure registry)

If the registry is behind HTTPS, there is nothing to configure. If the platform is open only over HTTP, you must configure the client side to allow HTTP. Docker allows localhost as an exception, so it works without configuration.

Add the host to /etc/docker/daemon.json and restart Docker.

{ "insecure-registries": ["mlops.example.com:10000"] }
sudo systemctl restart docker

The operator usually sets up nodes at installation. The procedure is in Installation.

The shared namespace mlops

Repositories whose path starts with mlops/ are in a reserved shared namespace. A system administrator uses it to build training runtime · dataset stager · serving builder images once and share them with several tenants.

Tenant tokenPlatform token (system administrator)
mlops/… pullOnly tags shared with the tenant. Tags and repositories not shared return not foundAll
mlops/… pushRejected (shared namespace is read-only for tenant tokens)Allowed
Own tenant spaceAllowedNone

So sharing is per tag. Even within the same mlops/training-runtime repository, tenant A may get only the yolo-cuda tag and tenant B only the rfdetr-cpu tag. The system administrator decides who gets which shared images (Administration guide).

Shared images: when the global admin publishes a shared variant, the image is built into the reserved namespace mlops/. Sharing works per tag, so tenant A gets yolo-cuda, tenant B gets rfdetr-cpu, and both get the stager and builder. Tenant C, which has nothing shared, can neither list nor pull, and pushes with tenant tokens are refused

You cannot put images into mlops/ by import (tar upload). An import always goes into your own tenant space.

Troubleshooting

SymptomCause and fix
http: server gave HTTP response to HTTPS clientAn HTTP registry, but the client tried HTTPS. Apply the insecure setting above
unauthorized / 401The token is wrong, expired or revoked. Issue a new one
push fails with 403 insufficient permissionThe token's issuer lacks the DevelopDEVELOPER · OPERATOR · ADMIN permission in that tenant (VIEWER · APPROVER)
push fails with denied: shared namespace is read-only for tenant tokensThe path starts with mlops/. Use a different first segment
A layer push fails with 413 BLOB_UPLOAD_INVALIDOne layer exceeds the server limit (10 GiB by default, GEO_MLOPS_REGISTRY_MAX_BLOB_BYTES)
pull returns not foundAnother tenant's image, an mlops/ tag not shared with you, or it really does not exist
A layer push is cut off midwayThe body size or time limit of a front proxy. Tell the operator, or hand it over as a file and import it on screen

Written for the platform as of 2026-10-02.

© MLOps Central Platform