Skip to content

Container Registry in CI/CD pipelines

This guide covers authenticating to Thalassa Cloud Container Registry from CI/CD pipelines. Prefer OIDC workload identity so pipelines never store long-lived registry secrets. Access credentials remain available when federation is not an option.

Credential scopes

Access credentials must include a container registry scope:

ScopeAccessTypical use
containerRegistryPush and pullBuild pipelines that push images
containerRegistry:allPush and pullSame as above
containerRegistry:pullPull onlyDeploy pipelines and promotion stages

Create separate credentials for build and deploy stages. Deploy pipelines should use pull-only scope.

User sessions and IAM policies can also grant push and pull on registry resources. See Access control for policy-based access.

When using OIDC token exchange, grant the federated identity the same registry scopes (containerRegistry or containerRegistry:pull) and ensure the service account has the required IAM roles.

OIDC workload identity (recommended)

For GitLab CI, GitHub Actions, and other OIDC-capable platforms, exchange a short-lived CI identity token for a Thalassa Cloud access token, then log in to the registry with username sts_token. No client secret is stored in CI variables.

Prerequisites:

  1. Create a service account with registry push (or pull) permissions.
  2. Configure an identity provider and federated identity so your pipeline can impersonate that service account.
  3. Store only non-secret identifiers as CI variables: organisation ID and service account ID.

GitLab CI example

variables:
  THALASSA_API: "https://api.thalassa.cloud"
  # Set THALASSA_ORGANISATION_ID and THALASSA_SERVICE_ACCOUNT_ID as CI/CD variables

build:
  image: docker:24
  services:
    - docker:24-dind
  id_tokens:
    THALASSA_ID_TOKEN:
      aud: https://api.thalassa.cloud
  before_script:
    - apk add --no-cache curl jq
    - |
      if [ -z "$THALASSA_ORGANISATION_ID" ] || [ -z "$THALASSA_SERVICE_ACCOUNT_ID" ]; then
        echo "THALASSA_ORGANISATION_ID and THALASSA_SERVICE_ACCOUNT_ID must be set"
        exit 1
      fi
      THALASSA_ACCESS_TOKEN=$(curl -s -X POST "${THALASSA_API}/oidc/token" \
        -H "Content-Type: application/x-www-form-urlencoded" \
        -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
        -d "subject_token=${THALASSA_ID_TOKEN}" \
        -d "subject_token_type=urn:ietf:params:oauth:token-type:id_token" \
        -d "organisation_id=${THALASSA_ORGANISATION_ID}" \
        -d "service_account_id=${THALASSA_SERVICE_ACCOUNT_ID}" \
        -d "access_token_lifetime=7200s" | jq -r '.access_token')
      echo "$THALASSA_ACCESS_TOKEN" | docker login registry.nl-01.thalassa.cloud \
        -u sts_token --password-stdin
  script:
    - docker build -t registry.nl-01.thalassa.cloud/acme-platform/my-app:$CI_COMMIT_SHA .
    - docker push registry.nl-01.thalassa.cloud/acme-platform/my-app:$CI_COMMIT_SHA

Full federation setup for GitLab is covered in Configuring Workload Identity Federation for GitLab CI. For GitHub Actions, see GitHub Actions.

Docker login with access credentials

Most CI systems support running docker login before push or pull:

docker login registry.nl-01.thalassa.cloud \
  -u "$REGISTRY_USERNAME" \
  -p "$REGISTRY_PASSWORD"

Store REGISTRY_USERNAME and REGISTRY_PASSWORD as protected CI variables.

Build and push workflow

A typical build pipeline with static credentials:

# Authenticate
docker login registry.nl-01.thalassa.cloud \
  -u "$REGISTRY_USERNAME" \
  -p "$REGISTRY_PASSWORD"

# Build
docker build -t my-app:$CI_COMMIT_SHA .

# Tag with registry path
docker tag my-app:$CI_COMMIT_SHA \
  registry.nl-01.thalassa.cloud/acme-platform/my-app:$CI_COMMIT_SHA

docker tag my-app:$CI_COMMIT_SHA \
  registry.nl-01.thalassa.cloud/acme-platform/my-app:latest

# Push
docker push registry.nl-01.thalassa.cloud/acme-platform/my-app:$CI_COMMIT_SHA
docker push registry.nl-01.thalassa.cloud/acme-platform/my-app:latest

Tag images with commit SHA or build ID for traceability. Avoid overwriting latest in production namespaces without a promotion step.

Pull-only deploy workflow

Deploy pipelines that only need to verify or promote images:

docker login registry.nl-01.thalassa.cloud \
  -u "$REGISTRY_USERNAME" \
  -p "$REGISTRY_PASSWORD"

docker pull registry.nl-01.thalassa.cloud/acme-platform/my-app:$IMAGE_TAG

Use a credential with containerRegistry:pull scope, or an OIDC federated identity limited to pull.

Service accounts

For long-running automation, create a service account and either:

  • Impersonate it via OIDC token exchange (recommended for CI), or
  • Create an access credential scoped to the minimum required registry permissions

Bind registry:developer or a custom IAM policy for API-level namespace management if the pipeline also creates namespaces.

Recommended practices

PracticeRationale
Prefer OIDC over static secretsRemoves long-lived registry credentials from CI
Separate build and deploy identitiesLimit blast radius if a deploy credential leaks
Pull-only for deployPrevents accidental image overwrites from deploy pipelines
Tag with immutable identifiersSHA or build ID enables rollback and audit
Use retention policiesAutomatically prune CI-generated tags to control storage costs

Related documentation