Skip to content

Docker Containers in GitHub Repo using GitHub Packages

Updated: at 06:16 PM

Estimated read time: 15 minutes

image

This tutorial will show you how to publish containers along with your code.

At the end of the tutorial, you will have

Table of contents

Open Table of contents

What are GitHub Packages?

It’s a GitHub feature that allows code to packaged as a docker/OCI containers among other things.

Prerequisites

Create an Image

ARG BASE_IMAGE=python:3.10.14
CMD ["python", "-c", "print('Hello World!')"]

save this to a file named Dockerfile and follow that with this command

docker build . -t ghcr.io/reinthal/swapi:latest

If you are coding along switch reinthal for you own username and swapi for your own repo.

Create a Classic PAT

Press the Profile image of the organization or individual for which you are configuring packages for in the top right corner of github, then:

coding dev illustration
Developer settings can be found if you scroll all the way down
Create a classic token
Enable read/write packages

copy your token! You will only see it once! Write it down in your editor/terminal

For example, save it in your .bashrc like so

echo 'export GH_TOKEN=ghp_GKHASGDAKSGDAKLSDG' >> ~/.bashrc && source ~/.bashrc

Authenticate to ghcr.io

echo $GH_TOKEN | docker login ghcr.io -u reinthal --password-stdin
WARNING! Your password will be stored unencrypted in /home/kog/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

Docker warns that token is saved in plain text. Thats OK as long as you do not share this machine with someone else who has access to the root account, docker or your user’s files.

Build image, Tag and Push

Run the following commands from the directory where you saved your docker file:

docker build .
...

ARG BASE_IMAGE=python:3.10.14
...
Step 2/2 : CMD ["python", "-c", "print('Hello World!')"]
 ---> Running in 0229b7c89a0d
 ---> Removed intermediate container 0229b7c89a0d
 ---> 5b00cb6d911a
 Successfully built 5b00cb6d911a

Then, it’s time to tag the image id 5b00cb6d911a! A tag can be anything you like! Me and my son like icecream for example:

docker tag 0ebf5e30330b ghcr.io/reinthal/swapi:icecream

Since this is the latest build of the docker container, lets also tag it with the latest tag:

docker tag 0ebf5e30330b ghcr.io/reinthal/swapi:latest

Then we push the image to the GitHub container registry ghcr.io:

docker push ghcr.io/reinthal/swapi:latest
The push refers to repository [ghcr.io/reinthal/swapi]
ad48ec53d13d: Layer already exists
ce9cc1eccce8: Layer already exists
4f93c48cb46d: Layer already exists
b43c1d914be6: Layer already exists
0a1f19c0d90d: Layer already exists
a8273e3b1197: Layer already exists
cbe4fb5e267b: Layer already exists
734c0f0b65c2: Layer already exists
8845ab872c1c: Layer already exists
d7d4c2f9d26b: Layer already exists
bbe1a212f7e9: Layer already exists
latest: digest: sha256:935410f51b1ebaebdac668fcc0b0d81332473789d1c7b76a3447050ec045a6ab size: 2635

We have pushed a container to GitHub container registry!

In GitHub, the container registry is part of the Packages feature. The container image with the tag ghcr.io/reinthal/swapi:latest is now a GitHub Package.

Configure Packages with A GitHub Repo

This next part was unintuitive for me. GitHub does not provide a link between the github package ghcr.io/USERNAME/PACKAGENAME and the repository github.com/USERNAME/REPONAME out-of-the-box and it has to be configured. To configure linking between the package and the repository, first go to the organization/individual account for which the github token was configured, in my case reinthal, then

Go To profiles
Then Packages
package settings...
Configure your repo to be associated with this package. Make the repo administrator to enable pushing from github action
ARG BASE_IMAGE=python:3.10.14
CMD ["python", "-c", "print('Hello World!')"]
LABEL org.opencontainers.image.source = "https://github.com/reinthal/swapi"
#  ^ ⊂(◉‿◉)つ Add the above line

and pair the desired repo with this package. In my case, I named the repo and the package the same, i.e swapi.

Automate Using GitHub Actions

Add the following file to your github repo to push semantically versioned container images to GitHub Packages/GitHub Container Registry:

.github/workflow/tag-and-push.yaml

name: Auto Tag Application
on:
  push:
    branches:
      - main
env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}
concurrency: production
jobs:
  Patch:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: "0"
      - name: Minor version for each merge
        id: taggerDryRun
        uses: anothrNick/github-tag-action@1.36.0
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          WITH_V: true
          DRY_RUN: true

      - name: echo new tag
        run: |
          echo "The next tag version will be: ${{ steps.taggerDryRun.outputs.new_tag }}"
      - name: echo tag
        run: |
          echo "The current tag is: ${{ steps.taggerDryRun.outputs.tag }}"
      - name: echo part
        run: |
          echo "The version increment was: ${{ steps.taggerDryRun.outputs.part }}"
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v2
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
      - name: Build and push Docker images
        # You may pin to the exact commit or the version.
        uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0
        with:
          # List of a customs host-to-IP mapping (e.g., docker:10.180.0.1)
          # Build's context is the set of files located in the specified PATH or URL
          context: . # optional
          push: true
          # Path to the Dockerfile
          # List of tags
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.taggerDryRun.outputs.new_tag }}, ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}, ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest # optional
          labels: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.labels}}

      - name: Minor version for each merge
        id: taggerFinal
        uses: anothrNick/github-tag-action@1.36.0
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          WITH_V: true

The above GitHub action will automatically tag the container image with semantic versioning, i.e v1.0.0, v.1.3.0 etc.

Summary

We have done the following

  1. Create an image
  2. Create a classic PAT GitHub Token
  3. Authenticate to ghcr.io
  4. Tag and push the image
  5. Configure packages in profile
  6. Automate using github actions

You have just learned how to publish packages in the form of container images. Go ahead and deploy these to your favorite OCI environment now! :)

Further Reading