Ken Muse

Publishing Images With GitHub Actions

Over the last few weeks, I’ve had multiple people ask how to use GitHub Actions to publish images. In truth, it’s not as challenging as it may seem. Most of the steps are no different from manually publishing the images. The basic workflow is the same with all registries. For most situations, you can use inexpensive hosted Linux runners (such as ubuntu-latest).

Publishing Images

To get started, you’ll need to create a basic workflow. GitHub can create one automatically. Click Actions, then choose New workflow and select the Publish Docker container suggestion. This workflow implements the basic steps required for working with registries:

  1. Install your tools. This is the first step I recommend for any build process. It ensures that you have the right version of the tools for the process. It also clearly communicates the versions and dependencies for anyone attempting to manually run the process. By default, everything you need is either on the native runner images OR encapsulated in an Action (assuming you’re running on Linux). The one exception is the cosign container signing tool. This can be installed with the Action sigstore/cosign-installer. Signing images is a recommendation that validates that the image has not been altered. It’s generally a recommended best practice.

  2. If you are creating images for multiple platforms, you may need to install the QEMU emulator using docker/setup-qemu-action. This enables you to emulate additional platforms. For example, I can use QEMU to create ARM64 images while running in an x86 Linux environment.

  3. Setup Docker Buildx using docker/setup-buildx-action. The build extensions enable you to create cross platform builds, run multiple processes in parallel, and improves the build experience.

  4. Login any authenticated registries you will be using with docker/login-action. This gives you access to those images (and with the right permissions, the ability to push images).

  5. Gather the metadata for the image. Typically, you want to associate the image to the source code, the commit version, etc. Thankfully, this can be automated using docker/metadata-action. This Action extracts the event properties from the workflow and creates appropriate metadata and tags.

  6. Build and push the image using docker/build-push-action. This creates an image and makes it available on the target registry.

  7. Finally, if you’re signing the container you will need to run cosign to create the appropriate signature and push it to the registry.

That’s it. You’re published.

Sample code

If you want to see how this is done, I have some working samples available which create an image containing a simple Python web application. Make sure to review this workflow. It is heavily commented to provide you with a step-by-step walk-through of everything I mentioned here. Hopefully it helps to de-mystify the process.

The currently available samples are:

  • Publishing to GitHub Container Registry. This builds a multi-platform image (amd64/arm64) with a simple Python-based web server and pushes that image to the owner’s GitHub Container Registry. This example also provides details on how to automate the cleanup of old images. This will teach you the basic principles outlined above.
  • Publishing to Azure Container Registry. This demonstrates how to publish to Azure Container Registry (ACR). It is a more advanced walkthrough that illustrates some additional techniques you can use in Actions:
    • Create and use step output variables
    • Mask dynamically created variables and outputs
    • Dynamically create secrets using the GitHub CLI
    • Using GitHub with Federated Identity Credentials in Azure Active Directory (OIDC)
    • Deploying Azure infrastructure (Bicep) and configuring permissions
    • Building and pushing an image to ACR using an OIDC credential

These examples are meant to be studied together. They will help you build a strong understanding of the processes and provide a deeper understanding of techniques you can use for more advanced scenarios.

Happy DevOp’ing!