Getting started with GitHub Container Registry (2024)

Recently I updated one of my images on DockerHub and the upload kept timing out. Not sure if it was a temporary thing because it eventually succeeded after a few hours. Later I made one more update and encountered the same issue. This got me wondering if there was some new DockerHub free user restriction (didn’t seem to be, but who knows) and also prompted me to check out GitHub Container Registry. I don’t mind paying for DockerHub, and I might in the end, but no harm in checking out alternatives – especially as I use GitHub for managing the source code anyways.

The documentation for GitHub Container Registry is a bit all over the place in my opinion. By which it does not straight away tell you what to do, so you need to read through a bit… and even then I felt it wasn’t complete. But hey, that’s probably just me being impatient or not reading well. Nevertheless I thought it would be a good idea to have a short write up here as a reference to myself and anyone else.

First off, GitHub Container Registry – which I am going to shorten as GHCR from now – needs to be enabled. Follow the steps in this article to do that. I completely missed this the first time and couldn’t figure out why my Docker pushes to GHCR were erroring. This is what I was getting by the way:

1

2

3

4

5

6

7

8

#73 exporting to image

#73 sha256:e8c613e07b0b7ff33893b694f7759a10d42e180f2b4dc349fb57dc6b71dcab00

#73 pushing layers 1.3s done

#73 ERROR: unexpected status: 403 Forbidden

------

> exporting to image:

------

error: failed to solve: rpc error: code = Unknown desc = unexpected status: 403 Forbidden

Second off, the name of this registry is ghcr.io and images must be prefixed with ghcr.io/. DockerHub hosted images too have a prefix – docker.io/ – just that it’s implied. If you miss the prefix the image gets pushed to DockerHub instead.

To give an example: my image on DockerHub is called rakheshster/stubby-unbound (OWNER/IMAGE); the same on GHCR is called ghcr.io/rakheshster/stubby-unbound (ghcr.io/OWNER/IMAGE).

Since Sept 2020 GHCR supports the Docker Image Manifest V2, Schema 2 image format, which means you can push multi-architecture images to it. Nice!

Next – images you push to GHCR are private by default so you have an additional step of making them public. Instructions for personal accounts are here; scroll down the same to find instructions for organizations. That page hightlights another thing – I wasn’t sure where the images I push are to be found. Initially I thought they’d be visible under the Packages section of my source repo (coz when you try to add a Package it does say you can add Docker images, and some documents mention that GHCR replaces Packages)… but no, GHCR images are present in the top level of your account, under Packages. This is on the same level as your Repositories. For a screenshot check out step 3 of this article.

Note that GHCR is still in beta and currently free for both public and private images. This is subject to change when it exists the beta period, but according to this blog post it sounds like public images will continue to be free even after the beta period. That’s one good reason to switch to GHCR. This is what GitHub used to do in the past for its repositories too, and of course they’ve got the $$$s to support the pull/ push bandwidth and larger storage requirements thanks to Microsoft.

It is possible to link the GHCR Docker image to the source repo. Follow the steps in this link. It’s even possible to do this automatically via a LABEL in the Dockerfile. Nice!

1

LABEL org.opencontainers.image.source=https://github.com/OWNER/REPO

So how do you push images to GHCR? Easy.

First you need to create a Personal Access Token (PAT) for yourself. (It’s similar to DockerHub – I have tokens for each of the devices I authenticate from). See this link on how to create the same. Looks like with the prior Packages incarnation of GHCR you could also use a GITHUB_TOKEN within GitHub Actions but that’s not currently supported. During the beta period at least, PAT is the sole way to authenticate. Step 1 of this article tells you what permissions to assign the PAT. That article suggests storing it in an environment variable and echoing it to the docker login command thus:

but you could put it in a file instead and cat that:

1

cat ~/PAT.txt | docker login ghcr.io -u USERNAME --password-stdin

or just paste it into the password prompt if that’s easier:

1

2

docker login ghcr.io -u USERNAME

# this will prompt for the password

And that’s it really. Once you login as above, you can push your image as you always do… just be mindful to add the ghcr.io/ prefix I mentioned earlier. And if you forget to login, no biggie… it will error! :)

I usually build and push thus:

1

docker buildx build --platform $ARCH -t ghcr.io/OWNER/IMAGE_NAME:VERSION -t ghcr.io/IMAGE_NAME:latest --progress=plain --push $(pwd)

But you could also push an already built image:

1

2

docker push ghcr.io/OWNER/IMAGE_NAME:VERSION

docker push ghcr.io/OWNER/IMAGE_NAME:latest

If you didn’t tag the image when building it, do a docker images to find its ID, and tag it thus:

1

2

docker tag ID ghcr.io/OWNER/IMAGE_NAME:VERSION

docker tag ID ghcr.io/OWNER/IMAGE_NAME:latest

Then push it as before.

Pulling images from GHCR is similar to from DockerHub, just prefix ghcr.io/ to the image name.

If you use GitHub actions things are straight-forward there too. I have the following to login to DockerHub for instance:

1

2

3

4

5

- name: Login to Docker Hub

uses: docker/login-action@v1

with:

username: ${{ secrets.DOCKER_USERNAME }}

password: ${{ secrets.DOCKER_PASSWORD }}

The equivalent for GHCR:

1

2

3

4

5

6

- name: Login to GitHub Container Registry

uses: docker/login-action@v1

with:

registry: ghcr.io

username: ${{ github.repository_owner }}

password: ${{ secrets.GHCR_TOKEN }}

Pushing is even more straightforward. As before, there’s nothing additional to do apart from prefixing the ghcr.io/. Here’s an example from crazy-max’s docker/build-push-action@v2 (highly recommended! It is now an official Docker action) from his examples section:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

- name: Login to DockerHub

uses: docker/login-action@v1

with:

username: ${{ secrets.DOCKERHUB_USERNAME }}

password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Login to GitHub Container Registry

uses: docker/login-action@v1

with:

registry: ghcr.io

username: ${{ github.repository_owner }}

password: ${{ secrets.CR_PAT }}

- name: Build and push

uses: docker/build-push-action@v2

with:

context: .

file: ./Dockerfile

platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x

push: true

tags: |

user/app:latest

user/app:1.0.0

ghcr.io/user/app:latest

ghcr.io/user/app:1.0.0

Simple. Login to each, build and push with multiple tags covering both registries. I’ll start doing the same for my Docker images I think, as I use GitHub Actions anyways and this is an easy next step.

Getting started with GitHub Container Registry (2024)

References

Top Articles
Latest Posts
Article information

Author: Virgilio Hermann JD

Last Updated:

Views: 6646

Rating: 4 / 5 (61 voted)

Reviews: 92% of readers found this page helpful

Author information

Name: Virgilio Hermann JD

Birthday: 1997-12-21

Address: 6946 Schoen Cove, Sipesshire, MO 55944

Phone: +3763365785260

Job: Accounting Engineer

Hobby: Web surfing, Rafting, Dowsing, Stand-up comedy, Ghost hunting, Swimming, Amateur radio

Introduction: My name is Virgilio Hermann JD, I am a fine, gifted, beautiful, encouraging, kind, talented, zealous person who loves writing and wants to share my knowledge and understanding with you.