Consume container images
Every image is private, so every consumer needs a credential that can read the owner. Use a token scoped to read:package only, so a leaked pull credential can never push or delete. For the registry reference, see Container registry. To publish, see Publish container images.
Create a read-only token
Section titled “Create a read-only token”Sign in as the user or machine account that will pull, and create a personal access token scoped to read:package. For unattended consumers (a server, a cluster, a deploy pipeline), use a dedicated machine user, not a personal account.
Pull from a workstation or server
Section titled “Pull from a workstation or server”echo "$CODEBAHN_TOKEN" | docker login codebahn.net -u "$USERNAME" --password-stdindocker pull codebahn.net/acme/api:1.4.0On a server, write the credential once into the Docker config and reuse it. The login stores it under ~/.docker/config.json. Protect that file with file permissions; treat it as a secret.
Pull in Kubernetes
Section titled “Pull in Kubernetes”Create an image pull secret from a read:package token, then reference it in the pod or service account.
kubectl create secret docker-registry codebahn-registry \ --docker-server=codebahn.net \ --docker-username="$USERNAME" \ --docker-password="$CODEBAHN_TOKEN"apiVersion: v1kind: Podmetadata: name: apispec: imagePullSecrets: - name: codebahn-registry containers: - name: api image: codebahn.net/acme/api:1.4.0Attach the secret to a service account if every pod in a namespace pulls from the registry, so you do not repeat imagePullSecrets on each workload.
Pull in another workflow
Section titled “Pull in another workflow”A Codebahn job that deploys or runs an image from its own owner pulls with the built-in ${{ github.token }}, the same credential used to publish. There is no secret to store.
jobs: deploy: runs-on: ubuntu-latest permissions: contents: read steps: - name: Log in to the registry run: echo "${{ github.token }}" | docker login codebahn.net -u "${{ github.actor }}" --password-stdin - run: docker pull codebahn.net/acme/api:${{ github.ref_name }}To pull an image owned by a different user or org, the built-in token does not reach across owners. Log in with a read:package machine-user token from a secret instead, and keep that job on protected branches; secrets are withheld from fork pull requests.
- name: Log in to the registry run: echo "${{ secrets.REGISTRY_PULL_TOKEN }}" | docker login codebahn.net -u "${{ vars.REGISTRY_USER }}" --password-stdinPulls from a runner in the same EU region as the registry do not count as internet egress, so in-CI consumption stays cheap.
Pin what you deploy
Section titled “Pin what you deploy”Pull an immutable tag (:1.4.0 or a commit SHA), never :latest, for anything you deploy. A moving tag means a redeploy can pull a different image than the one you tested, and a rollback cannot find the old one. Resolve to a digest if you want a deploy to fail closed when an image changes:
docker pull codebahn.net/acme/api@sha256:<digest>Rotate and scope
Section titled “Rotate and scope”- Use a separate
read:packagetoken per consumer, so you can revoke one without breaking the others. - Never reuse a
write:packagetoken to pull. A pull credential should not be able to push. - Rotate machine-user tokens on a schedule, and immediately if a node or config is compromised.