Cordium documentation · Latest

Building Container Images

Workspaces can build and push OCI container images with rootless Podman, without a Docker daemon and without any privileged access to the host (read more here). This example uses an ephemeral, auto-stopping Workspace to build an image from a repository's Dockerfile, push it to GitHub Container Registry, and print its digest.

The Template

The registry token is a Secret of the payments Space with the write:packages scope. The image, tag and Dockerfile are variables, so that the same Template can build any image of the Space's repositories:

cordium create secret ghcr-push-token.payments.cordium --from-env GHCR_TOKEN
spec: vars: - name: REPO value: acme-corp/payments-api - name: REF value: main - name: IMAGE value: ghcr.io/acme-corp/payments-api - name: TAG value: latest - name: DOCKERFILE value: Dockerfile repository: url: https://github.com/${{vars.REPO}} cloneOptions: depth: 1 singleBranch: true disableLazyUnshallow: true authentication: http: username: x-access-token password: fromSecret: github-read-token.payments.cordium runtime: autoStop: true envVars: - key: REGISTRY_USERNAME value: acme-bot - key: REGISTRY_TOKEN fromSecret: ghcr-push-token.payments.cordium tasks: - name: install-podman type: ON_CREATE runAsRoot: true onFailure: ON_FAILURE_ABORT run: | apt-get update apt-get install -y --no-install-recommends podman skopeo - name: build-and-push type: ON_CREATE workingDir: /workspace/repo onFailure: ON_FAILURE_ABORT run: | set -e git fetch -q --depth 1 origin "${{ vars.REF }}" git checkout -q FETCH_HEAD printf '%s' "$REGISTRY_TOKEN" | sudo podman login ghcr.io -u "$REGISTRY_USERNAME" --password-stdin sudo podman build --pull=always \ --file "${{ vars.DOCKERFILE }}" \ --label "org.opencontainers.image.revision=$(git rev-parse HEAD)" \ --tag "${{ vars.IMAGE }}:${{ vars.TAG }}" . sudo podman push --digestfile /tmp/digest "${{ vars.IMAGE }}:${{ vars.TAG }}" echo "Pushed ${{ vars.IMAGE }}@$(cat /tmp/digest)" limit: cpu: millicores: 8000 memory: megabytes: 16384 storage: megabytes: 40000

Here are a few notes about this Template:

  • sudo does not preserve the environment, which is why the token is piped to podman login rather than read by it from $REGISTRY_TOKEN.

  • REF can be a branch, a tag or a commit, which is fetched and checked out before the build. The repository URL is written as ${{vars.REPO}}, without spaces, since URLs are validated before the variables are substituted (read more here).

  • The image is built from the repository's own Dockerfile and context. Image builds use the Workspace's CPU, memory and storage, so size the limits for your largest image.

  • The run fails if any step fails, and the pushed digest appears in the Workspace's logs.

cordium create template image-build.payments.cordium --file image-build.yaml cordium create ws --template image-build.payments.cordium --ephemeral --start \ --var REF=v2.19.0 --var TAG=2.19.0
note

Since REGISTRY_TOKEN is an environment variable, it is readable by every process running in the Workspace, including the build's RUN steps of an untrusted Dockerfile. Only build images from repositories that you trust with this token, use a token that can only push to the intended packages, and prefer short-lived tokens where your registry supports them.

Using the Image in Workspaces

Images pushed this way can be used as the image of other Templates and Workspaces, which is an alternative to pre-builds for heavy toolchains that change rarely (read more here):

spec: image: registry: url: ghcr.io/acme-corp/payments-dev:2026.10 authentication: username: acme-bot password: fromSecret: ghcr-read-token.payments.cordium