DEV Community

Cover image for GitHub Container Registry: How to push Docker images to GitHub 🐋
webdeasy.de
webdeasy.de

Posted on • Originally published at webdeasy.de

GitHub Container Registry: How to push Docker images to GitHub 🐋

The GitHub Container Registry allows you to publish or host private and public Docker images. You can find out everything you need to know in this tutorial.

With its registry service GitHub Packages, GitHub provides a way for developers to host their own Docker images directly in GitHub. In my opinion, the name of GitHub is somewhat misleading: the umbrella term for the service is GitHub Packages. It contains a number of registries. These include the Docker Registry, which has been renamed the Container Registry in order to host all types of containers.

GitHub Packages Structure

Why not use Docker Hub? 🐋

The Docker Hub is the first port of call when it comes to Docker images – no question about it. In the free version, however, only one private image is available to each account and the number of pulls (200 pulls per 6 hours) is also limited. And this is where the GitHub Container Registry comes into play! 🙂

Push Docker Images into the GitHub Registry

To create a Docker image, we need a Docker image, which we have to create with a Dockerfile. You can create such a Dockerfile using the docker init command, for example. Once the file is available, we can build the image:

docker build -t frontend .
Enter fullscreen mode Exit fullscreen mode

Screenshot from the Docker build process

In the next step, we need to log in to the GitHub Container Registry. To do this, we need a personal access token from GitHub.

Generate Personal Access Token (PAT)

To generate a new PAT, navigate to GitHub Settings > Developer seettings > Personal access tokens > Tokens (classic) and create a new token. You must select “write:packages” and** “read:packages”** as the scope. You can adjust the duration of the token to your project or set it to unlimited.
Create GitHub Personal Access Token (PAT)
You should save the token, e.g. in your password manager. If you lose the token, you will have to regenerate it.

Now open a terminal on your computer and set your PAT in a variable (replace <YOUR_PAT> with your token):

# Set PAT into a variable
export GH_PAT=<YOUR_PAT>
Enter fullscreen mode Exit fullscreen mode

To log in to the GitHub Container Registry (ghcr.io), use the following command (replace <USERNAME> with your GitHub username).

# Login to the GitHub Container Registry using your PAT
echo $GH_PAT | docker login ghcr.io -u <USERNAME> --password-stdin
Enter fullscreen mode Exit fullscreen mode

If the PAT and your user name were correct, you should receive the message “Login Succeeded”.
Successful Docker login to the GitHub Container Registry (ghcr.io)
Next, we tag the image so that it also ends up in the correct registry. Replace <USERNAME> with your GitHub username, <IMAGE_NAME> with the desired name of the image and <TAG> with the desired tag (default: latest)

# Tag the builded docker image
docker tag frontend ghcr.io/<USERNAME>/<IMAGE_NAME>:<TAG>
Enter fullscreen mode Exit fullscreen mode

Now we can push the image into the registry. Here you have to replace the same variables as when tagging:

docker push ghcr.io/<USERNAME>/<IMAGE_NAME>:<TAG>
Enter fullscreen mode Exit fullscreen mode

If you have done everything correctly, you should receive such an output or no error message 😉
GitHub Docker Regsitry: Tagging and pushing Docker images
Congratulations! You have successfully pushed your first Docker image to the GitHub registry! 🎉
GitHub Packages
On this page you now have further options, such as changing the visibility or deleting the image.

Pulling GitHub Docker Images

Of course, you can now also use your Docker images from the registry. The path is the same as for pushing:

docker pull ghcr.io/<USERNAME>/<IMAGE_NAME>:<TAG>
Enter fullscreen mode Exit fullscreen mode

GitHub Actions Workflow for Docker Images in the GitHub Registry

Pushing Docker images manually is daft! That’s why there are CI/CD pipelines that we can implement with GitHub Actions, for example. For certain events, e.g. a new commit, we can automatically build the Docker image and publish it in the registry. In the following example, a Docker image is built and published when the frontend branch is pushed. Building and publishing is already done after the “Build and push Docker image” step. However, I have left the last two steps in for the sake of completeness, as this is a workflow of one of my projects and you may also have a use case for it 😉

In your GitHub repository, you can simply create a file under .github/workflows/cd.yml.

name: Continuous Deployment - Frontend

on:
  push:
    branches: ['frontend']

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push-image:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      attestations: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Log in to the Container registry
        uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

      - name: Build and push Docker image
        id: push
        uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

      - name: Install SSH client
        run: sudo apt-get update && sudo apt-get install -y openssh-client

      - name: Deploy to VM
        env:
          SSH_HOST: ${{ vars.SSH_HOST }}
          SSH_USER: ${{ secrets.SSH_USER }}
          SERVER_SSH_KEY: ${{ secrets.SERVER_SSH_KEY }}
          IMAGE_TAG: ${{ steps.meta.outputs.tags }}
        run: |
          echo "${{ env.SERVER_SSH_KEY }}" > key.pem
          chmod 600 key.pem
          ssh -i key.pem -o StrictHostKeyChecking=no ${{ env.SSH_USER }}@${{ env.SSH_HOST }} << 'EOF'
            docker login ${{ env.REGISTRY }} -u ${{ github.actor }} -p ${{ secrets.MY_PERSONAL_PAT }}
            docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}
            cd ~
            docker compose down frontend
            docker compose up -d frontend
          EOF
          # Clean up the private key.pem
          rm key.pem
Enter fullscreen mode Exit fullscreen mode

And don’t forget: Create the used secrets and variables in the repository settings. In this case:

  • Secrets:
    • MY_PERSONAL_PAT
    • SERVER_SSH_KEY
    • SSH_USER
  • Variables:
    • SSH_HOST GitHub Repository: Secrets and variables

GitHub Container Registry: Conclusion

That’s it! Building and publishing Docker images in the GitHub Docker Registry or GitHub Container Registry is very similar to Docker Hub. Only the login via the GitHub PAT is a little more complicated, but also offers advantages in terms of authorisation management, etc. Will you be using the GitHub Packages Registry in the future?

Top comments (0)