This guide walks you through publishing Docker images to GitHub's Container Registry (GHCR) manually and automating the process with GitHub Actions. It includes best practices, clear examples, and troubleshooting tips.
Introduction to GitHub Container Registry
GitHub Container Registry (GHCR) is part of GitHub Packages, enabling you to:
- Store and share Docker images alongside your code.
- Host public or private images under your GitHub profile or repository.
- Access images via
ghcr.io
for deployment or testing.
Images appear in the Packages tab of your GitHub profile or repository (https://github.com/users/YOUR_USERNAME/packages
).
Prerequisites
Before starting:
- Install Docker locally.
- Have a GitHub account and a repository with a
Dockerfile
. - Basic familiarity with GitHub and command-line tools.
Step 1: Authenticating with GitHub Container Registry
GHCR requires a Personal Access Token (PAT) for authentication, not a standard password.
Creating a Personal Access Token
- Navigate to GitHub:
- Click your profile picture (top-right) > Settings > Developer settings > Personal access tokens > Tokens (classic) > Generate new token (classic).
- Configure the token:
-
Name: e.g.,
ghcr-access
. - Expiration: Choose 30, 60, or 90 days for security (avoid "No expiration").
-
Scopes: Select:
-
write:packages
(to push images). -
read:packages
(to pull images). -
delete:packages
(optional, for deleting images).
-
-
Name: e.g.,
- Click Generate token and copy it immediately—it won’t be shown again.
Security Tip: Store the PAT securely (e.g., in a password manager) and never commit it to version control.
Logging In to GHCR
Run the following command to authenticate:
docker login ghcr.io -u YOUR_GITHUB_USERNAME -p YOUR_PERSONAL_ACCESS_TOKEN
- Replace
YOUR_GITHUB_USERNAME
with your GitHub username. - Replace
YOUR_PERSONAL_ACCESS_TOKEN
with the copied PAT.
Example:
docker login ghcr.io -u robiulhossain -p ghp_XXXXXXXXXXXXXXXXXXXXXXXXXX
Step 2: Building and Publishing a Docker Image
Follow these steps to build and push a Docker image manually.
1. Build the Docker Image
Assuming you have a Dockerfile
in your project directory, build the image:
docker build -t ghcr.io/YOUR_GITHUB_USERNAME/IMAGE_NAME:TAG .
-
Tag format:
ghcr.io/
(e.g.,/ : ghcr.io/robiulhossain/hello-world:latest
). - Use
.
to reference the current directory containing theDockerfile
.
Example:
docker build -t ghcr.io/robiulhossain/hello-world:latest .
2. Push the Image to GHCR
Push the built image to GHCR:
docker push ghcr.io/YOUR_GITHUB_USERNAME/IMAGE_NAME:TAG
Example:
docker push ghcr.io/robiulhossain/hello-world:latest
Step 3: Verifying the Published Image
Confirm the image was published and works as expected.
1. Check Local Images
List local Docker images to verify the build:
docker image ls | grep YOUR_GITHUB_USERNAME
This shows images tagged with your username (e.g., ghcr.io/robiulhossain/hello-world
).
2. Test Pulling and Running the Image
Remove the local image to test pulling from GHCR:
docker image rm -f ghcr.io/YOUR_GITHUB_USERNAME/IMAGE_NAME:TAG
Run the image to ensure it works:
docker run ghcr.io/YOUR_GITHUB_USERNAME/IMAGE_NAME:TAG
Example:
docker image rm -f ghcr.io/robiulhossain/hello-world:latest
docker run ghcr.io/robiulhossain/hello-world:latest
3. Verify on GitHub
- Visit your GitHub profile or repository.
- Go to the Packages tab (
https://github.com/users/YOUR_USERNAME/packages
). - Confirm the image (e.g.,
hello-world
) is listed.
Step 4: Automating with GitHub Actions
Automate building and pushing images using a GitHub Actions workflow. This ensures images are updated with every code change.
Setting Up Repository Secrets
Store sensitive data as GitHub Secrets:
- Go to your repository > Settings > Secrets and variables > Actions > New repository secret.
- Add these secrets:
-
GHCR_USERNAME
: Your GitHub username (e.g.,robiulhossain
). -
GHCR_TOKEN
: Your PAT withwrite:packages
andread:packages
scopes. -
DOTENV_FILE
(optional): Contents of your.env
file, if needed by your app.
-
Creating the Workflow
Create a file at .github/workflows/publish-ghcr.yaml
:
name: Build and Push to GHCR
on:
push:
branches: [main]
jobs:
build-andpush:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ secrets.GHCR_USERNAME }}
password: ${{ secrets.GHCR_TOKEN }}
- name: Write .env file
if: ${{ secrets.DOTENV_FILE != '' }}
run: echo "${{ secrets.DOTENV_FILE }}" > .env
- name: Generate image tag
id: vars
run: echo "tag=$(date +'%Y%m%d%H%M')" >> $GITHUB_OUTPUT
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: |
ghcr.io/${{ secrets.GHCR_USERNAME }}/hello-world:latest
ghcr.io/${{ secrets.GHCR_USERNAME }}/hello-world:${{ steps.vars.outputs.tag }}
cache-from: type=gha
cache-to: type=gha,mode=max
Workflow Explanation
-
Trigger: Runs on pushes to the
main
branch. -
Steps:
- Checkout code: Fetches the repository code.
- Set up Buildx: Enables advanced Docker builds (e.g., multi-platform support).
- Log in to GHCR: Authenticates using secrets.
-
Write .env file: Conditionally creates a
.env
file ifDOTENV_FILE
secret exists. -
Generate tag: Creates a timestamp-based tag (e.g.,
202504161230
). -
Build and push: Builds the image and pushes it with
latest
and timestamp tags. - Caching: Uses GitHub Actions cache to speed up builds.
Testing the Workflow
- Commit and push the workflow file to the
main
branch. - Go to the Actions tab in your repository to monitor the workflow.
- After completion, verify the new image in the Packages tab or by running:
docker run ghcr.io/YOUR_GITHUB_USERNAME/hello-world:latest
Troubleshooting Tips
-
Authentication errors: Ensure the PAT has
write:packages
andread:packages
scopes and hasn’t expired. -
Image not visible: Confirm the image tag matches
ghcr.io/
./ : -
Workflow failures:
- Check secret names (
GHCR_USERNAME
,GHCR_TOKEN
). - Verify the
Dockerfile
path in the workflow. - Review GitHub Actions logs for errors.
- Check secret names (
-
Large images: Optimize your
Dockerfile
with multi-stage builds or.dockerignore
to reduce image size.
Best Practices
-
Use specific tags: Avoid relying solely on
latest
; use versioned or timestamped tags for traceability. - Secure secrets: Regularly rotate PATs and limit their scopes.
-
Optimize images: Minimize layers and remove unnecessary files in the
Dockerfile
. - Test locally: Always test images locally before pushing to GHCR.
- Monitor quotas: GHCR has storage limits for free accounts; monitor usage in the Packages tab.
Conclusion
This guide covered:
- Manually publishing Docker images to GitHub Container Registry.
- Automating the process with GitHub Actions for seamless updates.
- Verifying and troubleshooting the workflow.
By integrating GHCR into your development pipeline, you can streamline container management and deployment.