GitHub Actions workflows in combination with GitHub Container Registry Package Visibility

4 min read

965 words

Last week my task was to set up a container image that we wanted to use to test the GitHub Container Registry (GHCR). We wanted to see if we could lower our building times for one of our CI jobs when using GitHub’s registry to pull from. The following is a description of an error we encountered and how we got rid of it. Additionally, I will talk about the different visibility types of container images or packages how they are called at GitHub which were a hurdle we had to take with the registry.

The workflow.yaml file we used looked something like the following:

name: CI

  - pull_request

    runs-on: ubuntu-latest
      - name: Checkout
        uses: actions/checkout@v2
      - name: Log in to the Container registry
        uses: docker/login-action@v1
          username: ${{ }}
          password: ${{ secrets.GITHUB_TOKEN }}
    - name: Pull Image from
      run: docker pull

First, we check out the repository and log into the GitHub Container Registry afterwards with our as the username and the GITHUB_TOKEN as the password. After a successful login, we’ll pull our container image from the registry. The steps coming afterwards are not subject to the error we encountered so these are left out in this article.

The Error

After triggering the workflow with a Pull Request the workflow quickly answered with an Error response from daemon: unauthorized when getting into the docker pull step. After searching for an answer in the GitHub docs and the internet we found out that our problem lays inside of the visibility type of the container image. I will go into further detail in the sections below.

Package Settings and Visibility

The problem happened with an image built and saved in a GitHub organization account. For regular packages in GHCR, there are two types available – public and private. There is also a third visibility type of internal available in the GitHub Enterprise Cloud plan. After creating and pushing a container image to GitHub Packages the default visibility type after creation is always set to private. This can be changed in the package settings. In the following the 3 options will be described and how they influence the ability to work with these packages in GitHub Actions.


Changing the package’s visibility to public is the easiest option of these 3 to work with. After making the package public it is visible to anyone and it can be pulled anonymously via This is good for public images of applications like web apps or web servers like nginx. While working with GitHub Actions this is also the easiest option of visibility since we don’t need to login to the registry first. So using a public repository is as straightforward as it gets. However, this was not a viable option for us.

Caution: When making a package public it cannot be made private again


The visibility option of private is the default one when a container image is created in the first place. Accessibility is restricted only to people who have been given the rights from the package owner in the package’s settings. When given access it is possible to pull or with advanced rights write/update or delete the package entirely. To pull or change the image via CLI or API one has to authenticate with the container registry first. Authentication to pull, update or delete the container image happens via a Personal Access Token (PAT). How to do that is described in the GitHub documentation. [1] Without authenticating first one will see the error response unauthorized from the beginning of this post. For GitHub Actions workflows GitHub recommends using the GITHUB_TOKEN instead which is available in the workflow.

Anyway using a private package in a GitHub Action workflow did not work even when the account that triggered the workflow and was used to authenticate with the container registry. The login to the registry even succeeded but the following pull step failed due to the unauthorized error shown above. Even after giving the repository’s GitHub Actions workflow access to the package [2], it was not possible to pull the image.


The solution came from a GitHub community answer [3]. The internal visibility type is the answer to our problem here. With this setting, the container image is visible within the whole organization or all the organizations in the enterprise. Every member will have at least read/pull access to the package. Write and Delete rights are then given via the package’s settings. The GitHub Actions documentation is pretty vague about what the internal visibility does differently than giving the user account permissions who triggers the workflow in the first place.


After setting the package’s visibility to internal it was possible for our workflow to successfully run. Anyway, we found out that the GitHub repository is not yet a better option to migrate to with our CI runs. Having the possibility to cache the container image would make a huge difference in speed. But since this is not (yet?) possible we will stay with our current setup.

Thank you for reading,

  1. Working with the Container registry - GitHub Docs ↩︎

  2. Configuring a package’s access control and visibility - GitHub Docs ↩︎

  3. Can’t pull private Docker image using GITHUB_TOKEN - #3 by mpdude - GitHub Actions - GitHub Support Community ↩︎