CTCaliberTek
Course: DevOps Foundations and CI/CD Pipelines · Lesson 5 of 8Log in to track your progress and earn a certificate when you finish the course.
DevOps

GitHub Actions: Build and Push a Docker Image to GitHub Container Registry

Published September 5, 2026·Updated October 29, 2026·9 min read
Like SaveLog in to like, save, and track progress.

Introduction

Once your tests pass, the next step in many pipelines is building a container image and publishing it so it can be deployed. GitHub Container Registry, ghcr.io, integrates with Actions and can authenticate with the built-in token, so there is no password to manage. This lesson builds that pipeline.

Prerequisites

  • A repository with a Dockerfile
  • The previous GitHub Actions lessons

Step-by-Step Guide

Step 1: Grant the right permissions

Pushing to the registry needs write access to packages, and checking out code needs read access to contents. Declare exactly these at the job level. The workflow authenticates with the automatically provided GITHUB_TOKEN, so no extra secret is needed for ghcr.io.

permissions:
  contents: read
  packages: write

Step 2: Log in to the registry

docker/login-action logs in to a registry. For ghcr.io, use the registry address, the user who triggered the run, and the token. Image names on ghcr.io must be lowercase, which the metadata action takes care of for you.

      - uses: docker/login-action@v4
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

Step 3: Generate tags and labels automatically

docker/metadata-action creates sensible image tags and labels from the branch, tag, or commit that triggered the run, so you do not hand-write them. This example tags with the branch name, the short commit SHA, and the semantic version when you push a version tag.

      - id: meta
        uses: docker/metadata-action@v6
        with:
          images: ghcr.io/${{ github.repository }}
          tags: |
            type=ref,event=branch
            type=sha
            type=semver,pattern={{version}}

Step 4: Build and push

Set up Buildx, then build and push with docker/build-push-action. Only push when the run is not a pull request, so pull requests prove the image builds without publishing anything. The complete workflow is shown in the next block.

name: Publish image
on:
  push:
    branches: [main]
    tags: ["v*"]
  pull_request:

permissions:
  contents: read
  packages: write

jobs:
  image:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: docker/setup-buildx-action@v4
      - uses: docker/login-action@v4
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - id: meta
        uses: docker/metadata-action@v6
        with:
          images: ghcr.io/${{ github.repository }}
          tags: |
            type=ref,event=branch
            type=sha
            type=semver,pattern={{version}}
      - uses: docker/build-push-action@v7
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

Step 5: Use and secure the published image

After a run, your image appears under the repository's Packages. Check the package's access settings, since you may need to link it to the repository or change its visibility. Pull it anywhere with docker pull ghcr.io/OWNER/REPO:TAG. For stronger supply chain security, pin the third-party actions you use to full commit SHAs and let Dependabot keep them updated.

docker pull ghcr.io/OWNER/REPO:main

FAQs

Why does the push fail with permission denied?
Check that the job has packages write permission, that the image name is lowercase, and that the package's settings allow the repository to write to it.
Can I push to Docker Hub instead?
Yes. Use the same login action with docker.io, your Docker Hub username, and a personal access token stored as a repository secret, never your account password.

Summary

Grant packages write, log in to ghcr.io with the GITHUB_TOKEN, let the metadata action produce tags and labels, and push only outside pull requests. The image then becomes the artifact your deployment uses.

Related tutorials

Comments

Log in or create a free account to join the discussion.

  • No comments yet. Be the first to comment.