- docs
- Integrations
- Authenticate to GHCR and ECR from CI
Authenticate to GHCR and ECR from CI
This page shows how to give an ocx job credentials for a private registry. It has a recipe each for GitHub Container Registry (GHCR) and Amazon ECR. The recipes need no terminal and leave nothing on a throwaway runner.
ocx reads credentials per registry. OCX_AUTH_* variables win, then the Docker configuration. Authenticate to a registry explains the layers. Here you pick the one that suits a runner.
Pick a method
Section titled “Pick a method”| Method | Use it when |
|---|---|
OCX_AUTH_<REGISTRY>_* variables | The token is in the CI secret store and the job runs only ocx. This is the default choice. |
ocx login --password-stdin | Another tool in the same job reads ~/.docker/config.json too. |
| A Docker credential helper | The runner is long-lived and a helper can mint short-lived tokens itself. |
Variables are scoped to one registry. The registry name in the variable replaces every non-alphanumeric character with _, so ghcr.io becomes ghcr_io. The reference lists the type, user and token variables.
If TYPE is omitted, ocx infers it: USER and TOKEN together mean basic, and TOKEN alone means bearer.
GitHub Container Registry
Section titled “GitHub Container Registry”GHCR takes your GitHub username as the user and a token as the password. In GitHub Actions the built-in GITHUB_TOKEN is enough for packages the repository can read. Grant the job packages: read, or packages: write to push.
Set the variables on the job, not on one step. The setup-ocx action pulls the toolchain before your own steps run, and it needs the credential to do so.
jobs: build: runs-on: ubuntu-latest permissions: contents: read packages: read env: OCX_AUTH_ghcr_io_TYPE: basic OCX_AUTH_ghcr_io_USER: ${{ github.actor }} OCX_AUTH_ghcr_io_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 - uses: ocx-sh/setup-ocx@v1 - run: ocx exec -- my-private-tool --versionA private package must also allow the repository in its package settings. A token that works for docker login ghcr.io works here.
To share the credential with other tools, log in once instead. Map the token into the step with env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}, because the runner exports GITHUB_ACTOR but not GITHUB_TOKEN:
echo "$GITHUB_TOKEN" | ocx login ghcr.io --username "$GITHUB_ACTOR" --password-stdin --allow-insecure-store--allow-insecure-store is explained below.
Amazon ECR
Section titled “Amazon ECR”ECR issues a password with the AWS CLI. The user is always AWS, and the password is valid for 12 hours, so fetch it inside the job. The registry host is <account>.dkr.ecr.<region>.amazonaws.com.
export OCX_AUTH_123456789012_dkr_ecr_eu_west_1_amazonaws_com_TYPE=basicexport OCX_AUTH_123456789012_dkr_ecr_eu_west_1_amazonaws_com_USER=AWSexport OCX_AUTH_123456789012_dkr_ecr_eu_west_1_amazonaws_com_TOKEN="$(aws ecr get-login-password --region eu-west-1)"ocx pullThe long names come from the host: 123456789012.dkr.ecr.eu-west-1.amazonaws.com with every dot and hyphen turned into _.
The login form writes the same credential to the Docker configuration:
aws ecr get-login-password --region eu-west-1 \ | ocx login 123456789012.dkr.ecr.eu-west-1.amazonaws.com --username AWS --password-stdin --allow-insecure-storeOn a runner that stays up, the Amazon ECR credential helper mints tokens on demand. Name it for the registry in ~/.docker/config.json, and ocx delegates to it:
{ "credHelpers": { "123456789012.dkr.ecr.eu-west-1.amazonaws.com": "ecr-login" } }Storing credentials on a runner
Section titled “Storing credentials on a runner”ocx login writes to the same ~/.docker/config.json as docker login. It prefers a credential helper or a keychain. A runner has neither, so the command refuses with exit 78 unless you pass --allow-insecure-store. That flag stores the credential as plaintext base64 in the config file.
Use it only on a runner that is destroyed after the job. The OCX_AUTH_* variables take precedence over a stored credential at read time. ocx logout exits 0 when no credential is stored, but a failed revocation exits non-zero. A cleanup step that must never fail needs || true.
Never bake a token into an image with ENV or ARG. The Docker page shows the BuildKit secret form.
When a job still gets 401
Section titled “When a job still gets 401”A refused credential exits 80 (AuthError). ocx login checks the credential against the registry first unless you pass --no-verify. Check these in order:
- The variable name matches the registry host after the
_substitution, including the account id and region for ECR. TYPEisbasicfor a user and password pair, andbearerortokenfor a bare token.- The token has the scope for the action. Pushing needs write access, and pulling needs read.
- A mirror in
[mirrors]needs its own credential. Auth for the mirror covers it.