Skip to main content

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

Written by:

August 9, 2021

0 mins read

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:

1docker 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:

wordpress-sync/blog-node-js-docker-hub-github-action

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:

1name: "Docker: Docker Hub"
2
3# This workflow uses actions that are not certified by GitHub.
4# They are provided by a third-party and are governed by
5# separate terms of service, privacy policy, and support
6# documentation.
7
8on:
9  push:
10    branches: [ main ]
11    tags: [ 'v*.*.*' ]
12  pull_request:
13    branches: [ main ]
14
15env:
16  REGISTRY: docker.io
17  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:

1jobs:
2  build_and_publish:
3
4    runs-on: ubuntu-latest
5    permissions:
6      contents: read
7      packages: write
8
9    steps:
10      - name: Checkout repository
11        uses: actions/checkout@v2
12
13      - name: Log into registry ${{ env.REGISTRY }}
14        if: github.event_name != 'pull_request'
15        uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
16        with:
17          registry: ${{ env.REGISTRY }}
18          username: ${{ secrets.DOCKERHUB_USERNAME }}
19          password: ${{ secrets.DOCKERHUB_TOKEN }}
20
21      - name: Extract Docker metadata
22        id: meta
23        uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
24        with:
25          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
26          flavor: |
27            latest=true
28            prefix=
29            suffix=
30
31      - name: Build and push Docker image
32        uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
33        with:
34          context: .
35          push: ${{ github.event_name != 'pull_request' }}
36          tags: ${{ steps.meta.outputs.tags }}
37          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:

wordpress-sync/blog-node-js-docker-hub-success

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:

1$ docker pull <user>/<repository>
2Using default tag: latest
3latest: Pulling from <user>/<repository>
4b4d181a07f80: Pulling fs layer
5de8ecf497b75: Pulling fs layer
669b92f9e5e70: Pulling fs layer
71f2b8e2c8ad8: Waiting
8d0f4259cb643: Waiting
99ae47f3f99ba: Waiting
1087270829eb60: Waiting
11905fc634546c: Waiting

Are you pushing vulnerable Docker images?

Why stop here? You can find, monitor, and even fix vulnerabilities in Docker images with Snyk. You can adopt whichever workflow that suits best, whether you prefer the CLI, or import Docker images directly from Docker Hub so you can monitor them for security vulnerabilities.

My colleague Eric Smalling wrote a detailed best practices guide on Developer Driven Workflows – Dockerfile & image scanning, prioritization, and remediation if you want to deepen your knowledge on this.Lastly, there’s a Snyk GitHub Action you can use in the GitHub Actions ecosystem to scan for security vulnerabilities in open source libraries, and container images.

Go deeper

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

  1. 10 best practices to containerize Node.js web applications with Docker

  2. Docker for Node.js developers: 5 things you need to know not to fail your security

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.

How to Build a Security Champions Program

Snyk interviewed 20+ security leaders who have successfully and unsuccessfully built security champions programs. Check out this playbook to learn how to run an effective developer-focused security champions program.