Cleaning up legacy private GHCR mirror images

We mirror upstream container images to GHCR so our CI does not depend on Docker Hub, quay.io, mcr.microsoft.com, or other registries at test time. This avoids rate limits and temporary upstream outages, and it also helps fork-based PRs because they can pull the mirrored images without needing upstream credentials.

Most mirror packages should be public today. The reason some old mirrored images are still private is historical: older workflow code had bugs and, in some cases, packages were created as private even though they should have been public. This is a one-time cleanup or migration task. Once the legacy private packages are either deleted or switched to public, current workflows should no longer recreate that problem because they publish via GITHUB_TOKEN.

Recommended option: use the cleanup workflow

Run the GitHub Actions workflow:

🧹 Images: Cleanup GHCR (Private)

Suggested steps:

  1. Open the repository’s Actions tab.
  2. Select 🧹 Images: Cleanup GHCR (Private).
  3. Click Run workflow.
  4. Keep visibility=private.
  5. Keep dry_run=true for the first run.
  6. Leave ghcr_namespace empty unless you want to target a different owner or organization.
  7. Leave ghcr_prefix=mirror unless you want to narrow the cleanup scope.
  8. Provide a cleanup token in the token field.
  9. Review the dry-run output.
  10. Run it again with dry_run=false to actually delete the legacy private mirror packages.

For this repository, the workflow resolves the effective package prefix as:

<repository>/mirror

So in practice it targets packages such as:

infinito-nexus-core/mirror/...

What token is needed?

For normal mirroring, no extra secret is needed. secrets.GITHUB_TOKEN is enough.

For deleting packages, you need a classic GitHub Personal Access Token with:

delete:packages

That is the important permission for removing old private GHCR packages.

The current workflow accepts that token directly via the token input.

Alternative: manually change package visibility

If there are only a few affected packages, you can also fix them manually in GitHub:

  1. Open the user or organization Packages page.
  2. Open the affected container package.
  3. Click Package settings.
  4. Under Danger Zone, click Change visibility.
  5. Change the package from Private to Public.

This is useful if you want to keep the package instead of deleting it.

Important: once a package is made public, GitHub does not allow changing it back to private.

Which option should I use?

  • Use the cleanup workflow if you want to remove stale legacy private mirror packages in bulk.
  • Use manual visibility changes if you want to keep a small number of existing packages and simply correct their visibility.

Again, this should only be necessary once. These private mirror packages are legacy artifacts from older buggy behavior, not something that should continue to happen with the current workflow setup.