GHCR 403 Forbidden
-
Root Cause of the
403 ForbiddenError -
Manual Package Creation: The error occurs when a developer builds and pushes the initial container image manually from a local terminal using a Personal Access Token (PAT).
- Strict Account Ownership: When pushed manually, GitHub assigns package ownership strictly to the individual user profile (
ritikkumar27) rather than associating it with a specific code repository. - Automated Token Restriction: Even if the
.github/workflows/deploy.ymlpipeline specifiespermissions: packages: write, GitHub's default security model protects user-owned packages. It prevents the repository's automated bot token (${{ secrets.GITHUB_TOKEN }}) from overwriting a user-owned package without explicit authorization, resulting in a403 Forbiddenbuild failure.
-
Manual Pushes vs. GitHub Actions Linking
-
Automated Actions Behavior: When an image is built and pushed directly by a GitHub Actions workflow, GitHub detects that the token originated from a specific repository and automatically links the resulting container package to that repository's sidebar dashboard.
- Manual Terminal Behavior: Pushing from a laptop terminal bypasses repository context, dropping the package under global profile packages in an unlinked state.
- The Dockerfile Metadata Solution: To force repository linkage during manual local pushes, developers can add a standard metadata label near the top of the
Dockerfile: - This explicit label instructs GHCR which repository owns the image regardless of where the push originated.
- Step-by-Step Resolution (Granting Pipeline Access)
To resolve the 403 Forbidden error and allow the CI/CD pipeline to push updates, package permissions must be adjusted in GitHub:
- Go to your GitHub Profile and click the Packages tab.
- Click on the astro_portfolio package.
- On the right-hand sidebar, select Package settings.
- Scroll to the Manage Actions access section and click Add Repository.
- Search for the
astro_portfoliocode repository and add it. - Change the Access Role: In the dropdown next to the added repository, change the permission role from
Readto Write or Admin.
Once this role is updated, subsequent workflow runs will succeed without permission rejection.
- Public vs. Private Visibility & Server Pull Permissions
In addition to push permissions, package visibility determines how the homelab server pulls the image during deployment:
- Private Packages (Default): Newly created GHCR packages are private by default. To pull a private image during deployment (
docker compose pull), the homelab server must first authenticate using a PAT with theread:packagesscope viaecho "$TOKEN" | docker login ghcr.io -u <username> --password-stdin. - Public Packages: If visibility is changed to Public under Package Settings, the homelab server can execute
docker compose pullanddocker compose up -dover the open network without requiring any server-side login credentials.