Skip to content
Latchkey

Docker "403 Forbidden" from ECR/GHCR auth in CI

ECR and GHCR return 403 when the token is expired or the identity lacks permission. ECR auth tokens are short-lived, and GHCR needs the right package scope on the token.

What this error means

A pull or push to an ECR or GHCR repository fails with 403 Forbidden or denied. It often follows a stale login or a token without the right scope.

docker
error: failed to push 1234.dkr.ecr.us-east-1.amazonaws.com/app:latest:
unexpected status from PUT request to https://1234.dkr.ecr.us-east-1.amazonaws.com/v2/app/manifests/latest: 403 Forbidden

Common causes

Expired ECR auth token

ECR login tokens last ~12 hours; a long job or a reused login can outlive the token.

Insufficient IAM or package permission

The IAM role lacks ecr:PutImage/BatchGetImage, or the GHCR token lacks write:packages.

Repository policy denies the principal

An ECR repository policy or GHCR package visibility blocks this identity.

How to fix it

Refresh ECR auth and check IAM

  1. Re-run ecr get-login-password right before the push.
  2. Ensure the role has the required ECR actions.
Terminal
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 1234.dkr.ecr.us-east-1.amazonaws.com

Scope the GHCR token correctly

  1. Grant packages: write (or a PAT with write:packages) and re-login.
.github/workflows/build.yml
permissions:
  packages: write

How to prevent it

  • Refresh ECR tokens immediately before use, grant least-privilege ECR/GHCR permissions, and check repository policies. A 403 from auth is deterministic, so a retry alone will not fix credentials.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →