GHCR: Complete Guide to GitHub Container Registry in April 2026
April 10, 2026 by Gecko Security Team
GHCR guide for April 2026: Learn GitHub Container Registry authentication, CI/CD integration, security, and how to push/pull images with unlimited pulls.
You switched to GHCR because storage is free and you're tired of Docker Hub's pull limits breaking your builds. Then you spend an afternoon figuring out why docker push fails with permission denied even though you authenticated successfully, why your organization packages need SSO authorization on tokens, and which combination of read:packages, write:packages, and repo scopes you actually need. The authentication system connects to GitHub's existing permissions in ways that create non-obvious failure modes.
TLDR:
- GHCR stores container images at ghcr.io with free unlimited pulls and tight GitHub Actions integration
- Authentication uses GitHub PATs with package scopes or automatic GITHUB_TOKEN in workflows
- GHCR has no rate limits unlike Docker Hub's 200 pulls per 6 hours, making CI/CD more reliable
- Registry scanning catches CVEs but misses business logic flaws in containerized application code
- Gecko finds authorization bypasses and access control bugs that exist in your containers at runtime
What Is GHCR and How Does GitHub Container Registry Work
GHCR (GitHub Container Registry) is GitHub's native container image storage service, accessible at ghcr.io. If you're already using GitHub for source control, GHCR lets you store and distribute OCI-compliant container images in the same ecosystem where your code lives.
GHCR replaced the older Docker package support that lived under docker.pkg.github.com. The registry treats container images as first-class artifacts tied directly to repositories or organizations, not generic packages. You get better visibility, access controls inherited from your repository settings, and tighter coupling with GitHub Actions workflows.
When you push an image to ghcr.io, it's stored as an OCI artifact that any Docker-compatible client can pull. The registry follows OCI specifications, so you can use standard Docker commands or tools like Podman. Images can be public or private, scoped to your user account or organization, and linked to specific repositories for automatic visibility and permission inheritance.
Understanding GHCR Pricing: Current Free Status and Future Billing Structure
GHCR currently operates under a different billing model than other GitHub Packages offerings. Container image storage and bandwidth are free, making it one of the more accessible registry options if you're already in the GitHub ecosystem.
This creates confusion because GitHub Packages (for npm, Maven, NuGet, and RubyGems) follows a tiered billing structure with included storage and transfer limits. GHCR doesn't fall under those same billing rules. According to GitHub's official documentation, the Container Registry remains free for now.
GitHub has committed to providing at least one month advance notice before introducing any pricing changes. If you're considering GHCR for production workloads, this gives you time to react, but it's worth factoring into long-term infrastructure planning.
One detail that matters for CI/CD: bandwidth used when pulling or pushing images within GitHub Actions workflows stays free regardless of future billing changes. Your automated pipelines won't rack up transfer costs even if external bandwidth gets metered later.
GHCR vs Docker Hub: Comparing Container Registry Options
Docker Hub remains the default registry for many teams, but GHCR changes the calculation if you're already working in GitHub's ecosystem. The core difference comes down to rate limits and integration friction.
Docker Hub's free tier restricts you to 200 container pulls every six hours for anonymous users, which breaks CI/CD pipelines fast. GHCR gives you unlimited pulls for public images and no artificial throttling on private repositories tied to your GitHub account.
Access control works differently too. Docker Hub manages permissions separately from your source code, so you're maintaining two sets of rules. GHCR inherits repository permissions directly from GitHub, meaning anyone who can read your repo can pull images scoped to it.
For GitHub Actions workflows, GHCR authentication happens automatically through the built-in GITHUB_TOKEN. Docker Hub requires separate credential management and secret storage.
Feature | GHCR (GitHub Container Registry) | Docker Hub |
|---|---|---|
Rate Limits (Public Images) | No rate limits on public or private image pulls | 200 pulls per 6 hours for anonymous users; 200 pulls per 6 hours for free authenticated accounts |
Storage & Bandwidth Pricing | Currently free with no storage or bandwidth charges; GitHub committed to 1-month advance notice before any pricing changes | Free tier includes unlimited public repositories; private repositories limited by plan tier |
Authentication in GitHub Actions | Automatic authentication via built-in GITHUB_TOKEN; no separate credential management needed | Requires manual secret storage and separate credential management for workflow authentication |
Access Control Model | Inherits permissions directly from GitHub repository settings; unified access management with source code | Separate permission system independent of source code repositories; requires dual maintenance |
Image Namespace Structure | ghcr.io/NAMESPACE/IMAGE where namespace matches GitHub username or organization | docker.io/NAMESPACE/IMAGE with separate Docker Hub account namespace |
Public Image Discovery | No centralized browse interface; discovery through GitHub package search or repository links | Centralized search and browse interface with filtering by categories and popularity |
CI/CD Pipeline Impact | Unlimited pulls prevent pipeline throttling; tight GitHub Actions integration reduces configuration overhead | Rate limits frequently break CI/CD pipelines on free tier; requires paid plans or pull-through caching strategies |
Authenticating to GHCR: Personal Access Tokens and GitHub Actions Integration
Authentication to ghcr.io relies on GitHub's native credential system. Local development requires a Personal Access Token with appropriate package scopes, while GitHub Actions workflows use the automatically provided GITHUB_TOKEN.
Generate a PAT from Developer Settings > Personal Access Tokens. Reading public and private images requires the read:packages scope. Pushing containers needs write:packages. When images tie to private repositories, add the repo scope for proper permission inheritance.
Authenticate Docker or Podman with your token:
echo YOUR_PAT | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin
The --password-stdin flag prevents tokens from appearing in shell history. Podman uses identical syntax.
For GitHub Actions, skip manual PAT creation. The GITHUB_TOKEN secret handles GHCR authentication automatically:
- name: Log in to GHCR
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
This workflow-scoped token expires when the job completes, limiting credential exposure.
Pushing and Pulling Container Images from GHCR
Image names on GHCR follow the structure ghcr.io/NAMESPACE/IMAGE_NAME:TAG, where the namespace matches your GitHub username or organization. Tag and push your local image:
docker tag myapp:latest ghcr.io/youruser/myapp:v1.0.0
docker push ghcr.io/youruser/myapp:v1.0.0
Pulling public images requires no authentication:
docker pull ghcr.io/open-webui/open-webui:main
Private images need your PAT or GITHUB_TOKEN first. GHCR supports multi-architecture manifests via Docker buildx:
docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/youruser/myapp:latest --push .
Image visibility defaults to private. You can switch to public in package settings without exposing your repository if it's private, though testing for hidden attack vectors remains important.
Integrating GHCR with GitHub Actions for CI/CD Pipelines
GitHub Actions workflows authenticate to GHCR using the built-in GITHUB_TOKEN, which eliminates separate credential management. The workflow context injects repository names and commit metadata for automatic tagging.
A typical workflow grants package write permissions and logs in before building:
name: Build and Push to GHCR
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
- name: Login to GHCR
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build and push
run: |
docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
docker push ghcr.io/${{ github.repository }}:${{ github.sha }}
The permissions block is required for push operations. Omitting it causes authentication failures despite valid tokens. For deployments spanning multiple registries like Docker Hub or AWS ECR, store additional credentials as repository secrets and execute sequential login steps before tagging images with registry-specific prefixes.
Container Registry Security Risks and Vulnerability Management
Container registries store more than just images. They inherit every vulnerability, hardcoded secret, and misconfiguration baked into the layers you push. 87% of container images contain high or critical security vulnerabilities, which creates immediate exposure when pulling from GHCR without scanning.
Base image vulnerabilities compound across your stack. An outdated Alpine or Ubuntu layer propagates the same CVEs to every dependent image. Exposed secrets like API keys or database credentials embedded in layers remain accessible even after deletion, since registry layers are immutable and cached across pulls.
Supply chain attacks target registries directly. Typosquatting, compromised upstream images, and malicious tags inserted into public namespaces turn trusted sources into infection vectors. GHCR's public image discoverability increases attack surface if you're pulling third-party containers without verification.
Registry scanning catches known CVEs but misses business logic flaws in the applications running inside containers.
Searching and Finding Public Images on GHCR
GHCR doesn't offer a centralized search interface like Docker Hub's browse page. You can't land on ghcr.io and browse popular images or filter by categories. Discovery happens through GitHub's package search or direct repository links.
Search for public GHCR images from GitHub's main search bar using the type:package filter. Add registry constraints like ghcr.io open-webui to narrow results. Repository README files often include ghcr.io pull commands, making the source repo your best discovery path.
Package visibility settings live in repository or organization package settings, not at push time. Switching an image from private to public doesn't expose the linked repository if it remains private. You control package and code visibility independently.
Verify authenticity by checking the package's linked repository and organization. GHCR displays the source repository for each image, letting you confirm the maintainer matches expectations before pulling, similar to how authorization bypass vulnerabilities require verification of authentication flows.
Common GHCR Troubleshooting: Authentication, Permissions, and Rate Limits
Authentication failures usually trace back to token scope. If docker login ghcr.io succeeds but pushes fail with "denied: permission_denied", your PAT is missing write:packages. Pulling private images linked to private repos needs the repo scope added.
Organization accounts with SSO require token authorization. After generating your PAT, click "Configure SSO" next to the token and authorize it for your organization. Without this step, you'll authenticate successfully but hit permission errors on org-scoped packages.
Permission denied errors during push often mean the package namespace doesn't match your authentication context. Pushing to ghcr.io/orgname/image requires membership in orgname with package write access. Verify the namespace matches your username or an organization you belong to.
GHCR doesn't impose Docker Hub-style rate limits on public image pulls, but GitHub Actions workflow failures can stem from insufficient job permissions. Add packages: write to the job's permissions block when pushing images.
Securing Container Images: Beyond Registry Scanning with Business Logic Testing
Registry scanning tools catch CVEs in base images and dependencies, but they can't analyze what happens when your containerized application runs. A clean scan on your GHCR image tells you nothing about authorization bypasses, privilege escalation paths, or broken access control in the application code inside that container.
Container scanning operates at the dependency layer, checking Alpine versions, library CVEs, and known vulnerabilities in packages. Static analysis struggles with business logic vulnerabilities in your application code. Business logic flaws live in your application code. They come from how services interact across trust boundaries, how authentication flows between microservices, and whether permission checks actually exist where they should.
Gecko finds semantic vulnerabilities that scanning misses by analyzing how containerized applications actually behave and tracing execution paths across service boundaries to identify authorization gaps that only appear at runtime.
Final Thoughts on GHCR Security and Implementation
GHCR solves the Docker Hub rate limit problem and integrates cleanly with your GitHub workflows, but container security needs more than vulnerability scanning. Your business logic flaws live in how services trust each other and handle permissions, not in outdated base images. Gecko traces those execution paths across your containerized stack to catch semantic vulnerabilities that registry tools miss.
FAQ
How do I authenticate Docker to GHCR for the first time?
Generate a Personal Access Token from GitHub Developer Settings with read:packages and write:packages scopes, then run echo YOUR_PAT | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin to authenticate without exposing your token in shell history.
What's the main difference between GHCR and Docker Hub for CI/CD workflows?
GHCR has no rate limits on public image pulls and authenticates automatically through GitHub Actions' built-in GITHUB_TOKEN, while Docker Hub restricts free accounts to 200 pulls every six hours and requires separate credential management.
Why does my GitHub Actions workflow fail to push images even after successful authentication?
Your workflow job needs explicit packages: write permission in the permissions block. Authentication alone isn't enough, and missing this permission causes push operations to fail despite valid tokens.
Can container registry scanning catch authorization flaws in my containerized application?
No. Registry scanning only detects CVEs in base images and dependencies, not business logic vulnerabilities like broken access control or missing authorization checks in your application code that run inside the container.
When will GitHub start charging for GHCR storage and bandwidth?
GHCR remains free right now, and GitHub has committed to at least one month advance notice before introducing any pricing changes. Bandwidth used within GitHub Actions workflows will stay free regardless of future billing.




