Skip to content

Access tokens and CI

An access token lets a CI pipeline or a tool outside Kupe use the registry. Choose pull, or push and pull, and an expiry between 1 and 365 days (90 by default). The token’s secret is shown once, when you create it. Kupe keeps no copy, so if you lose it, revoke the token and create another.

Pods in your clusters do not need a token: connect the cluster instead.

Creating an access token needs a tenant admin. readonly members can list tokens but not create them.

Terminal window
kupe registry token create --name github-actions --access push --expires-in-days 30 -o json

-o json prints the token’s ID, username, access, expiry and secret. Without it, standard output holds only the secret, so TOKEN=$(kupe registry token create --name deploy --access pull) works in a script. The command waits until the token is issued, which takes seconds; a token that is not issued within a minute is revoked. See kupe registry token create.

A registry holds up to 50 access tokens. Login sessions from kupe registry login do not count towards that.

Sign in with the username shown with the token, which looks like kupe+acme+tok-1a2b3c4d5e6f7a8b, and the secret as the password. The username has no characters a shell treats specially, so it needs no quoting.

The secret starts with kupecr_ and is 49 characters long, so secret scanners can recognise one that leaks. Store it in your CI system’s secret store, masked in logs.

Terminal window
printf '%s' "$KUPE_REGISTRY_TOKEN" | docker login registry.kupe.cloud \
--username "$KUPE_REGISTRY_USERNAME" --password-stdin
Access Can do
pull Pull every repository in your registry.
push Pull, push to every repository, and replace existing tags. Shown as Push and pull in the console.

Neither can delete images. Deleting is done through the console, the CLI, the API or Terraform; see Managing images.

A token stops working at its expiry time. The console shows how many days are left, and the RegistryAccessTokenExpiring alert fires when a token is due to expire within 7 days (see Alerts). A token cannot be extended: create a new one, give it to whatever used the old one, then revoke the old one.

Last used shows the most recent pull or push made with the token. It is checked about once an hour. The registry keeps its record of pulls and pushes for 30 days, so a token with no use in that time shows Not in the last 30 days in the console.

Revoking a token stops it signing in. Admins can revoke any token; a readonly member can revoke only their own login sessions.

Terminal window
kupe registry token list
kupe registry token revoke rt-1a2b3c4d5e6f7a8b

In the console, select Revoke on the token’s row. The API is DELETE .../registries/registry/tokens/{id}.

A token belongs to the member who created it, or to the member who created the API key that created it.

  • When that member leaves the tenant, every access token and login session they created is revoked.
  • When an admin becomes a readonly member, the access tokens and login sessions they created that can push are revoked. Their pull-only ones keep working.

For a pipeline that must outlive one person’s membership, plan who creates its token and replace it when that person leaves.

This workflow builds the image in the repository and pushes it on every push to main. Create a push token, then set the repository variables KUPE_TENANT and KUPE_REGISTRY_USERNAME and the secret KUPE_REGISTRY_TOKEN. The hello-kupe sample holds the same file as ci/github-actions.yaml.

# Builds the image in this repository and pushes it to your Kupe registry
# on every push to main.
#
# Set in the repository's settings (Settings > Secrets and variables > Actions):
# variable KUPE_TENANT your tenant name
# variable KUPE_REGISTRY_USERNAME the access token's username (kupe+<tenant>+tok-...)
# secret KUPE_REGISTRY_TOKEN the access token's secret (kupecr_...)
# Create a push token with:
# kupe registry token create --name github-actions --access push -o json
name: Push to the Kupe registry
on:
push:
branches:
- main
permissions:
contents: read
jobs:
push:
name: Build and push
runs-on: ubuntu-latest
env:
# Change hello-kupe to the repository name you push to.
IMAGE: registry.kupe.cloud/${{ vars.KUPE_TENANT }}/hello-kupe
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- name: Sign in to the Kupe registry
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
registry: registry.kupe.cloud
username: ${{ vars.KUPE_REGISTRY_USERNAME }}
password: ${{ secrets.KUPE_REGISTRY_TOKEN }}
- name: Build and push
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
context: .
platforms: linux/amd64
push: true
tags: ${{ env.IMAGE }}:${{ github.sha }}

This job builds the image and pushes it on every push to the default branch, using Docker in Docker. Set the CI/CD variables KUPE_TENANT, KUPE_REGISTRY_USERNAME and KUPE_REGISTRY_TOKEN, each protected, and mask the token. The hello-kupe sample holds the same file as ci/gitlab-ci.yml.

# Builds the image in this repository and pushes it to your Kupe registry
# on every push to the default branch.
#
# Set in the project's settings (Settings > CI/CD > Variables), each protected:
# KUPE_TENANT your tenant name
# KUPE_REGISTRY_USERNAME the access token's username (kupe+<tenant>+tok-...)
# KUPE_REGISTRY_TOKEN the access token's secret (kupecr_...), also masked
# Create a push token with:
# kupe registry token create --name gitlab-ci --access push -o json
push-to-kupe:
image: docker:29.9.2
services:
- docker:29.9.2-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
# Change hello-kupe to the repository name you push to.
IMAGE: registry.kupe.cloud/$KUPE_TENANT/hello-kupe:$CI_COMMIT_SHORT_SHA
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
- printf '%s' "$KUPE_REGISTRY_TOKEN" | docker login registry.kupe.cloud --username "$KUPE_REGISTRY_USERNAME" --password-stdin
- docker build --platform linux/amd64 -t "$IMAGE" .
- docker push "$IMAGE"