Skip to main content

Pushing Custom Images to Docker Hub Using GitHub Actions

Pushing Custom Images to Docker Hub Using GitHub Actions
Table of Contents

In the previous article I built a custom nginx image and pushed it to Docker Hub by hand: build, tag, login, push. That works exactly once. The second time you forget the tag, the third time a teammate pushes from a laptop with a stale base image, and soon nobody can say which commit produced the image running in production. The fix is boring and reliable: let GitHub Actions do the build and push on every commit to master.

What you’ll build
#

  • A GitHub Actions workflow that builds and pushes a Docker image to Docker Hub on every push to master
  • Docker Hub credentials stored as GitHub secrets, kept out of the repository
  • Two tags per build: latest plus the GitHub run number, so every image traces back to a specific build
  • A look at the run status in the Actions tab and the published tags on Docker Hub

1. Prerequisites
#

You need two things before starting:

  • An application that is already Dockerized. I am reusing the simple setup from the last article, two files: index.html and a Dockerfile. The example project is here on GitHub.

  • A Docker Hub account with your username and an access token.

2. Adding Secrets in GitHub
#

The workflow needs your Docker Hub credentials, and they belong in GitHub secrets, never in the workflow file itself. In your repository, go to Settings > Secrets > Actions and click New repository secret. Create two secrets:

  • DOCKERHUB_TOKEN — your Docker Hub access token.

  • DOCKERHUB_USERNAME — your Docker Hub username.

Github Adding Secret

Github Action Adding New Secret

GitHub Action Adding New Secret

Watch out: use an access token here, not your account password. A token can be scoped and revoked on its own; a leaked password gives away the whole account.

With the secrets in place, add the workflow file to the repository like below:

Github Action Build File in Repository

3. Setting Up the GitHub Actions Workflow
#

Create a new file at .github/workflows/docker-image.yml with the following contents:

name: Nginx Build
 
on:
  push:
    branches: master
    paths:
    - '**'
jobs:
  main:
    runs-on: ubuntu-22.04
    steps:
      -
        name: Checkout
        uses: actions/checkout@v2
      -
        name: Set up QEMU
        uses: docker/setup-qemu-action@v2
      -
        name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      -
        name: Login to DockerHub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      -
        name: Build and push
        id: docker_build
        uses: docker/build-push-action@v3
        with:
          context: .
          file: Dockerfile
          push: true
          tags:  ${{ secrets.DOCKERHUB_USERNAME }}/my-custom-nginx:latest, ${{ secrets.DOCKERHUB_USERNAME }}/my-custom-nginx:${{github.run_number}}
          secrets: |
            GIT_AUTH_TOKEN=${{ secrets.DOCKERHUB_TOKEN }}
      -
        name: Image digest
        run: echo ${{ steps.docker_build.outputs.digest }}

This workflow runs each time code is pushed to the master branch:

  • Checkout: Pulls the latest code from the repository.

  • Set up QEMU: Installs QEMU for building images for different architectures.

  • Set up Docker Buildx: Enables advanced build options in Docker.

  • Log in to Docker Hub: Uses your stored credentials to log in.

  • Build and Push Image: Builds the image and tags it with both latest and the GitHub run number, so each build gets its own version tag.

  • Show Image Digest: Outputs the digest after a successful build.

4. Tagging the Docker Image
#

The workflow tags the image in two ways:

  1. latest: This tag will always point to the most recent image.

  2. ${{github.run_number}}: A unique tag based on the GitHub Actions run number, giving each build its own version.

Key point: never publish only latest. A latest-only repository cannot be rolled back and cannot tell you which build a running container came from. The run-number tag costs nothing and answers both questions.

5. Monitoring the Workflow
#

Once you push the workflow file to the master branch, GitHub Actions automatically triggers the build. You can view the status under the repository’s Actions tab. A yellow dot shows a build in progress, and tags are updated upon successful completion.

Github Repository Showing Building Status

Github Repository Showing Building Status

The below screenshot shows how tags get updated in any build.

Github Action Build Running Status 2

Github Action Build Running Status 2

Below is the screenshot from a successful run.

Github Action Build Running Status

Github Action Build Running Status

6. Viewing the Results
#

After the successful push, Docker Hub shows two tags for the image: latest and 5. The 5 is the build number from GitHub Actions, which means I can pull that exact image back at any time or point a rollback at it.

Here’s a screenshot of the published image tags:

Docker Published Image Tags History

Docker Published Image Tags History

Key takeaways
#

  • Once an image gets pushed more than once, automate it. The workflow above is about 40 lines and removes the laptop from the release path.
  • Credentials live in GitHub secrets and are scoped through a Docker Hub access token, not a password.
  • Tag every build with something traceable (github.run_number here) alongside latest; rollbacks depend on it.

What’s next
#

Take the example repo, swap in your own Dockerfile, and push a commit; you should see both tags appear on Docker Hub within a couple of minutes. The same workflow shape works for other registries too. Point docker/login-action at ACR or ECR credentials and change the tag prefix, and you have the identical pipeline against a private registry.

Related

Pushing Docker Image to Docker Hub

··4 mins
A custom image that only exists on your laptop is not shipped; it is a local experiment. The moment a teammate or a server needs to run it, the image has to live in a registry. Docker Hub is the one most people start with, and the push workflow is the same shape you will later use against Azure Container Registry or Amazon ECR, so it is worth doing once by hand before automating it. This post walks through that manual path with a small nginx-based image.