How to publish Node.js Docker images to Docker Hub registry using GitHub Actions | Snyk

By Liran Tal

In a previous post, we presented a step-by-step tutorial on how to publish Node.js Docker images to GitHub Packages registry using GitHub Actions. In this post, we’ll focus on publishing the Docker image that we build to the public Docker Hub registry.

Why is this useful you might ask? The Docker command line application docker has a default registry setting for docker.io which points to the Docker Hub registry. This provides a nice developer experience of pulling images by the <user>/<repository> convention, such as the following, if you wanted to use the Snyk CLI as a Docker image: 

docker pull snyk/snyk-cli

In this article, we’ll go through a step-by-step walkthrough of configuring another Docker image build and publish workflow, which can work side-by-side with the previous workflow we presented for GitHub Packages registry.

Just like the previous post, this one follows up on Dockly, the open source Node.js command line tool for managing docker containers from the CLI, and shows how to push a Docker image to Docker Hub. You can view the final workflow in Dockly’s GitHub repository

Create a GitHub Actions workflow 

To add a new workflow, you can simply create a new file in the .github/workflows/ directory, or browse to your open source repository on GitHub, click the Actions tab, and then click on New Workflow and begin a new workflow:

A GitHub Actions workflow status

Choose the filename for the Docker Hub publish workflow file, such as docker-publish-to-dockerhub.yml.

If you’re doing this from the GitHub UI, it may pre-populate the workflow file, in which case just remove it and add the following:

name: "Docker: Docker Hub" # This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation. on: push: branches: [ main ] tags: [ 'v*.*.*' ] pull_request: branches: [ main ] env: REGISTRY: docker.io IMAGE_NAME: ${{ github.repository }}

We have outlined each of these in detail in the previous article, but to recap:

  • This Docker Hub workflow will run on every push, tags release, and pull request made to the main branch of the repository.
  • The global environment variables define the REGISTRY to docker.io which means our images will be pushed and available from Docker Hub, and the IMAGE_NAME set to the GitHub username and repository name. If you have a different Docker Hub username, then you might need to tweak this one.

Next, we’ll define our jobs, in which we will establish a process of building the Docker image, and then publishing it.

Login to Docker Hub and build and publish the Docker image

Next, we will need to perform the following actions:

  1. Login to Docker Hub, for which we will need to create a token in Docker Hub and set it up as a repository secret in the GitHub repository settings.
  2. Extract metadata of the Docker image that we build, so it is available to the build process of the image.
  3. Push the Docker image that was built to Docker Hub.

The above is established with the following code snippet, which needs to be added to the workflow contents that we created in the previous step:

jobs: build_and_publish: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - name: Checkout repository uses: actions/checkout@v2 - name: Log into registry ${{ env.REGISTRY }} if: github.event_name != 'pull_request' uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c with: registry: ${{ env.REGISTRY }} username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Extract Docker metadata id: meta uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} flavor: | latest=true prefix= suffix= - name: Build and push Docker image uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc with: context: . push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }}

Once you applied the workflow file and merged it to the main branch, you should hopefully end up with a successful build that results in a new Docker image pushed to the Docker Hub  registry:

A successful deploy of a Docker base image to the Docker Hub registry, from a GitHub Actions workflow

A known caveat is that, at this point in time, the Docker Hub repository that you publish the image to doesn’t automatically populate with a proper description and README contents, because this is not available in the official docker/build-and-push GitHub Action.

If you’d like to seek this capability out, the maintainers recommend the marketplace’s GitHub Action Docker Hub Description and point to an example reference in their docs which you can adopt.

Pulling Docker images from Docker Hub registry

To fetch the new Docker image from Docker Hub, all you need to do is run the familiar docker pull command, such as:

$ docker pull <user>/<repository>
Using default tag: latest
latest: Pulling from <user>/<repository>
b4d181a07f80: Pulling fs layer
de8ecf497b75: Pulling fs layer
69b92f9e5e70: Pulling fs layer
1f2b8e2c8ad8: Waiting
d0f4259cb643: Waiting
9ae47f3f99ba: Waiting
87270829eb60: Waiting
905fc634546c: Waiting

Go deeper 

If you’d like to further explore best practices for building Docker images, I suggest the following:

And if you didn’t read the previous article and would like to publish Docker images to GitHub Packages, then read up on how to publish Node.js Docker images to GitHub Packages registry using GitHub Actions.

Sign up for Snyk to secure your code, dependencies, containers, and IaC.