Skip to content
Latchkey

GitHub Actions "ghcr.io push denied" (packages: write)

Pushing a container image to the GitHub Container Registry with GITHUB_TOKEN requires packages: write. Without it, the push is denied at the registry. This is a permission/config gap, not a transient failure.

What this error means

docker push to ghcr.io fails with a denied/permission error after a successful docker login using GITHUB_TOKEN.

github-actions
denied: installation not allowed to Write organization package
Error: buildx failed: error pushing ghcr.io/owner/image:tag

Diagnose it: what token do you actually have?

Permission failures in Actions are almost never about your repository settings alone. Three things combine: the default GITHUB_TOKEN permission set for the repo or organization, the permissions: block in the workflow, and whether the event is a fork pull request, which downgrades the token to read-only regardless of everything else.

.github/workflows/ci.yml
- name: Show the token scopes actually granted
  run: |
    curl -sI -H "Authorization: Bearer $GITHUB_TOKEN" \
      https://api.github.com/ | grep -i "^x-oauth-scopes\|^x-accepted"
    echo "event: ${{ github.event_name }}"
    echo "fork PR: ${{ github.event.pull_request.head.repo.fork }}"
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Common causes

Missing packages: write permission

The job does not grant packages: write, so GITHUB_TOKEN can authenticate but not push.

Org package write policy blocks the repo

The organization package settings do not allow this repository to write the package.

How to fix it

Grant packages: write and log in correctly

  1. Add packages: write to the job permissions.
  2. Log in to ghcr.io with GITHUB_TOKEN before pushing.
  3. If the package already exists, ensure the repo has write access under the package settings.
.github/workflows/publish.yml
  publish:
    permissions:
      packages: write
      contents: read
    steps:
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

Grant the narrowest permission that works

Declaring a permissions: block switches the job from the repository default to exactly what you list, so an incomplete block is a common cause of a new failure right after someone tightened security. List every scope the job needs, not just the one that failed.

.github/workflows/ci.yml
permissions:
  contents: read        # checkout
  packages: write       # push to GHCR
  id-token: write       # OIDC to a cloud provider
  pull-requests: write  # comment on or label a PR
  checks: write         # publish check runs

How to prevent it

  • Set packages: write on any job that pushes to ghcr.io.
  • Grant the repository write access on existing org-owned packages.

Frequently asked questions

What causes GitHub Actions "ghcr.io push denied" (packages: write)?
There are 2 common causes: missing packages: write permission and org package write policy blocks the repo. The job does not grant packages: write, so GITHUB_TOKEN can authenticate but not push.
How do I fix GitHub Actions "ghcr.io push denied" (packages: write)?
Grant packages: write and log in correctly. Add packages: write to the job permissions.
What does GitHub Actions "ghcr.io push denied" (packages: write) actually mean?
docker push to ghcr.io fails with a denied/permission error after a successful docker login using GITHUB_TOKEN.
How do I stop GitHub Actions "ghcr.io push denied" (packages: write) happening again?
Set packages: write on any job that pushes to ghcr.io. The prevention section lists 2 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card