Skip to content

kupe registry command reference

kupe registry turns on your tenant’s container registry, connects clusters to it, creates access tokens, signs Docker in, and browses or deletes repositories, images and tags. A tenant has one registry, always named registry, so no command takes a registry name. For the model behind these commands, see Registry. To install the CLI, see Kupe CLI.

Every command also takes the CLI’s global flags (--tenant, --context, --config, --token, --api-url, --quiet, --verbose, --no-color), and kupe registry <command> --help prints its flags and examples.

Command Does
create Turn on the registry and wait until it is ready
get, list Show the registry
update Change the storage limit or deletion protection
delete Delete the registry and every image in it
wait Wait for the registry to be ready or deleted
connect CLUSTER Let a cluster’s pods pull, and optionally give namespaces a push Secret
connection list, get, update, rotate Inspect connections, change push namespaces, rotate credentials
disconnect CLUSTER Remove a cluster’s connection
repo list, get, delete Browse and delete repositories
image list, get, delete Browse and delete images
tag delete Remove a tag; the image stays
token create, list, get, revoke Access tokens; the list also shows login sessions
login Sign Docker in for 12 hours
logout Remove the credential from Docker and end your login sessions
kupe registry create [--storage-limit LIMIT] [--no-deletion-protection] [--wait=false] [--wait-timeout DURATION] [-o FORMAT]
Flag Default Description
--storage-limit the plan’s cap Most the registry may store, in Mi, Gi or Ti, such as 5Gi.
--no-deletion-protection off Create without deletion protection, which is otherwise on.
--wait true Wait until the registry is ready to push to and pull from.
--wait-timeout 15m Give up waiting after this long.
-o, --output table table, json, yaml or go-template=...
Terminal window
kupe registry create
kupe registry create --storage-limit 5Gi
kupe registry get [-o table|json|yaml|go-template=...]
kupe registry list [--phase PHASE] [-o table|wide|json|yaml|name|go-template=...]
Name: registry
Phase: Running
Endpoint: registry.kupe.cloud/acme
Storage: 1.2 GiB of 5.0 GiB
Storage Limit: plan
Repositories: 2
Deletion Protection: on
Created: 2026-10-19T10:00:00Z (2d ago)

Storage is what the registry stores, of the limit in force. Storage Limit is your own limit, or plan when the plan’s cap applies. list prints the same as a table of at most one row, for scripts that treat every service alike. -o wide adds PROTECTED and LIMIT.

kupe registry update [--storage-limit LIMIT | --clear-storage-limit] [--deletion-protection|--deletion-protection=false] [-o FORMAT]
Flag Description
--storage-limit New storage limit, in Mi, Gi or Ti, at or below your plan’s cap.
--clear-storage-limit Remove your limit, so the plan’s cap applies.
--deletion-protection --deletion-protection turns it on and --deletion-protection=false off.

Only the flags you pass are changed, and the change is in effect when the command returns. If another client changed the registry at the same moment, the CLI reads it again and retries once.

kupe registry delete [--yes] [--wait=false] [--wait-timeout DURATION]

Deleting the registry deletes every image in it, and they cannot be restored. On a terminal you type the registry’s name, registry, to confirm. --yes skips the prompt and is required in non-interactive environments. The command waits until the registry is gone (--wait-timeout, default 15m).

A registry with deletion protection on is never deleted:

Error: registry "registry" has deletionProtection enabled: set it to false before deleting
turn deletion protection off first: kupe registry update --deletion-protection=false
kupe registry wait [--for ready|deleted|pending|provisioning|degraded|terminating] [--timeout DURATION]

--for ready, the default, waits for phase Running, and --timeout defaults to 30m. On success it prints registry/registry running or registry/registry deleted.

kupe registry connect CLUSTER [--push-namespace NAMESPACE]... [--secret-name NAME] [--wait=false] [--wait-timeout DURATION] [-o FORMAT]
Flag Default Description
--push-namespace none A namespace that gets a push Secret. Repeat for each, up to 10.
--secret-name kupe-registry-push Name of the push Secret in each push namespace. It cannot change later.
--wait true Wait until the connection is Ready.
--wait-timeout 5m Give up waiting after this long.
-o, --output table table, json, yaml or go-template=...
Terminal window
# Pull only
kupe registry connect prod
# Pull, and let the ci namespace push
kupe registry connect build --push-namespace ci

Every pod in the cluster can then pull from the registry without imagePullSecrets. A push Secret can pull from and push to every repository in your registry, including replacing existing tags. See Connect a cluster.

kupe registry connection list [-o table|wide|json|yaml|name|go-template=...]
kupe registry connection get CLUSTER [-o FORMAT]
kupe registry connection update CLUSTER [--push-namespace NS]... | [--add-push-namespace NS]... [--remove-push-namespace NS]... | --clear-push-namespaces
kupe registry connection rotate CLUSTER [--yes] [--wait=false] [--wait-timeout DURATION] [-o FORMAT]
CLUSTER PHASE PUSH-NAMESPACES PULL-USERNAME ROTATION AGE
prod Ready none kupe+acme+pull-prod none 2d
build Ready ci kupe+acme+pull-build none 2d

-o wide adds PUSH-SECRET, PUSH-USERNAME and MESSAGE. get shows one connection, then a table of its push namespaces with each one’s phase and message.

update changes the push namespaces: --push-namespace replaces the whole list, --add-push-namespace and --remove-push-namespace edit it, and --clear-push-namespaces removes every push Secret, leaving pull only. It waits until every listed namespace is Ready and every removed one is withdrawn (--wait-timeout, default 5m).

rotate replaces the connection’s pull credential and push Secrets, and waits until the new ones are in place (--wait-timeout, default 10m). It asks for confirmation on a terminal, which --yes skips.

kupe registry disconnect CLUSTER [--yes]

Removes the cluster’s connection: its pull credential and every push Secret it delivered are withdrawn. It asks for confirmation on a terminal, which --yes skips. The request is asynchronous.

kupe registry repo list [--search TEXT] [--page N] [--page-size N] [--all] [-o FORMAT]
kupe registry repo get REPOSITORY [-o FORMAT]
kupe registry repo delete REPOSITORY [--yes]
REPOSITORY IMAGES PULLS UPDATED
team-a/api 4 120 1h
hello-kupe 1 3 18h

repo also answers to repos, repository and repositories. Lists show 25 to a page, or up to 100 with --page-size, and --all shows every page. IMAGES counts every image, each platform image of a multi-platform image as well as the multi-platform image itself. -o wide adds PULL-REFERENCE and CREATED. delete removes the repository with every image and tag in it. On a terminal you type the repository’s name to confirm.

kupe registry image list REPOSITORY [--tagged | --untagged] [--page N] [--page-size N] [--all] [-o FORMAT]
kupe registry image get REPOSITORY:TAG|REPOSITORY@sha256:DIGEST [-o FORMAT]
kupe registry image delete REPOSITORY@sha256:DIGEST [--yes]
DIGEST TAGS SIZE PLATFORMS PUSHED
sha256:906eba7069b0 v1.4.2, latest 46.0 MiB linux/amd64, linux/arm64 1h
sha256:09ef6aa2d53f v1.4.1 23.0 MiB linux/amd64 17h
sha256:cba06b5736fa untagged 22.0 MiB linux/amd64 10d

-o wide adds TYPE, LAST-PULLED and FULL-DIGEST. --untagged lists only images no tag points at. An image can be named as REPOSITORY:TAG, REPOSITORY@sha256:DIGEST, or the full reference you push to, registry.kupe.cloud/<tenant>/REPOSITORY:TAG.

get prints the image with its pull references:

Repository: team-a/api
Digest: sha256:906eba7069b06e719efc9b176bf7e23db8b68c267d5fcc5aa150e56fdef3c274
Type: index
Tags: v1.4.2, latest
Size: 46.0 MiB
Platforms: linux/amd64, linux/arm64
Pushed: 2026-10-22T08:30:00Z (1h ago)
Last Pulled: never
Pull By Digest: registry.kupe.cloud/acme/team-a/api@sha256:906eba7069b06e719efc9b176bf7e23db8b68c267d5fcc5aa150e56fdef3c274
Pull By Tag: registry.kupe.cloud/acme/team-a/api:v1.4.2, registry.kupe.cloud/acme/team-a/api:latest

delete takes a digest only and removes the image with every tag on it. A platform image of a multi-platform image cannot be deleted on its own: delete the multi-platform image instead.

kupe registry tag delete REPOSITORY:TAG [--yes]

Removes one tag. The image it pointed at stays, and so do its other tags. An image left with no tag is listed by kupe registry image list REPOSITORY --untagged.

kupe registry token create --name NAME --access pull|push [--expires-in-days DAYS] [--wait=false] [--wait-timeout DURATION] [-o text|json]
kupe registry token list [--kind token|session|all] [-o table|wide|json|yaml|name|go-template=...]
kupe registry token get ID [-o FORMAT]
kupe registry token revoke ID [--yes]

Creating a token needs a tenant admin.

Flag Default Description
--name required A name to recognise the token by, 1 to 128 characters.
--access required pull, or push, which includes pull.
--expires-in-days 90 Days until the token stops working, 1 to 365.
--wait true Wait until the platform has issued the token before printing it.
--wait-timeout 1m Give up and revoke the token after this long.
-o, --output text text prints the secret alone on standard output; json prints everything, the secret included.

The secret, kupecr_…, is printed once. On a terminal the token’s ID, username, access and expiry, and the docker login command, follow on standard error.

Terminal window
# Pull token for a deploy pipeline
TOKEN=$(kupe registry token create --name deploy --access pull)
# Push token for CI, valid 30 days, everything as JSON
kupe registry token create --name github-actions --access push --expires-in-days 30 -o json

token list shows access tokens and login sessions, newest first, never a secret:

ID NAME KIND ACCESS CREATED-BY EXPIRES LAST-USED
rt-0a1b2c3d4e5f6a7b github-actions token push alice@acme.example in 27d 1h ago
rt-1122334455667788 deploy token pull alice@acme.example in 88d never
rt-99aabbccddeeff00 kupe registry login session push alice@acme.example in 10h 1h ago

-o wide adds PHASE, USERNAME and AGE. revoke ends an access token or a login session. Admins can revoke any, and a readonly member only their own login sessions. See Access tokens and CI.

kupe registry login [--docker-config DIR] [--no-store -o json]

Signs Docker in with a login session that lasts 12 hours: push and pull for an admin, pull for a readonly member. The credential is stored as docker login would store it, through the credential helper your Docker configuration names for the registry (credHelpers, then credsStore), or in the configuration file itself, with a warning. --docker-config DIR uses DIR/config.json.

--no-store -o json prints the server, username, password and expiry, and stores nothing, for Podman, Buildah or another client.

A login session is for a person signed in with kupe auth login. With an API key the login is refused:

Error: login sessions are for people signed in with kupe; use an access token for automation
sign in with: kupe auth login
for automation, use an access token: kupe registry token create --name NAME --access pull|push
kupe registry logout [--docker-config DIR]

Removes the registry credential from Docker, as docker logout would, and revokes your own live login sessions on every machine you signed in on. Access tokens are not touched. A session that cannot be revoked is reported as a warning, and it still expires 12 hours after it was made.

Exit Meaning
0 Done
2 Missing or malformed flags, a storage limit above your plan’s, more than 10 push namespaces in a connection, or a token expiry out of range
3 A login with an API key (session_requires_user), or a token requested by a member who is not an admin
4 The tenant has no registry, or the repository, image, tag, connection or token does not exist
5 Deletion protection is on, the registry or cluster connection already exists, 20 push namespaces across the registry, 50 access tokens, a platform image of a multi-platform image, the registry is not ready yet, your plan includes no registry, or something is being deleted
7 The API is temporarily unavailable, or a token or login session was not issued within a minute
8 A wait ran out of time
130 Interrupted while waiting