Create Automated CI/CD Builds Using GitHub Actions and DockerHub
Continuous integration (CI) and continuous delivery (CD) incorporate the operating principles used by application development teams to deliver the application code changes through built-in automation, testing, and collaboration workflows. It eliminates the manual development workflows to create automated processes that are predictable and repeatable.
This eliminates human interventions and reduces workflow errors while ensuring code quality. Teams can frequently implement small code changes that will be automatically tested and pushed for delivery and deployment.
You can streamline your workflow using GitHub Actions and automate CI/CD Builds. This guide will teach you how to leverage GitHub Action to automate Docker builds to DockerHub. You will build an automated CI/CD builds using GitHub Actions and DockerHub.
Prerequisites
To follow along with this article, it is essential to have the following:
- Node.js installed on your computer.
- Docker installed on your computer.
- GitHub and DockerHub accounts.
- Basic knowledge of working with Docker.
- Basic knowledge of how to use GitHub.
- Familiarity with YAML file.
GitHub Actions and DockerHub
CI/CD approach creates a pipeline between integrations and delivery cycles. The pipeline’s packaged application is easily integrated with different tools to check code and automatically deploy to production.
GitHub Actions is one of the popular CI/CD tools. It allows you to automate workflows through code builds, tests, and code deployment through GitHub repositories. GitHub Actions can run tests, build Docker images and deploy applications to cloud infrastructures.
DockerHub is a container registry service for storing and distributing Docker images. A Docker image packages the application setting, dependencies, and codebase to create a portable artifact that is easier to integrate between different CI/CD pipeline phases.
By leveraging DockerHub and GitHub Actions, you can build, test, and deploy Docker-based applications and create a CI/CD pipeline. Let’s dive and create a workflow to do exactly that.
Creating the Application and a Dockerfile to Build the Application Image
To get started, you need an application that Docker will use to build an image. The same application should have the correct instructions to instruct Docker on how to package and build the application image. In this guide, you will develop a simple Node.js application with a single endpoint that just print Hello World
to the screen. Alternatively, if you have an application you have already developed, you can use it along and achieve the same objective this guide aims for.
The simple Node.js application will be created as follows:
From your preferred working directory, initialize the application:
npm init -y
Install express for setting up the web server:
npm install express
Create an app.js
file to host the application logic as follows:
- Import the necessary packages:
const express = require('express');
- Define the express instance and the port for the application:
const app = express();
const PORT = process.env.PORT || 4000;
- Define a default testing route:
.get('/',(req,res) => {
app.status(200);
res.send("Hello World!!");
res; })
- Start the application:
.listen(PORT, () => console.log(`App listening on port ${PORT} `)); app
The whole code in the apps.js looks as shown below:
const express = require('express');
const app = express();
const PORT = process.env.PORT || 4000;
.get('/',(req,res) => {
app.status(200);
res.send("Hello World!!");
res;
})
.listen(PORT, () => console.log(`App listening on port ${PORT} `)); app
- In the
package.json
add a script for running the application:
"start":"node app.js"
You can test your application and ensure it works as expected by starting the development server using the following command:
npm run start
From your browser, go to http://localhost:4000
to check if the application works as expected.

To run this application with Docker, you need to provide the correct command for packaging it. To do this, you can use a Dockerfile, which specifies the instructions to build a Docker image. Once the image is built, it contains everything required to run the application, including the code, runtime, libraries, and settings. This results in a Docker executable package that can be used to run the application on any Docker-supported platform.
In your application directory, create a file named Dockerfile
. In this Dockerfile
, add the commands for building the image step by step as follows:
Specify the base image to use, in this case, the node version:
FROM node:19
Define the working directory, where the application will reside inside the Docker:
WORKDIR /usr/src/app
Copy package.json
to the working directory:
COPY package*.json .
Run the npm install
command to install the application dependencies on Docker:
RUN npm install
Copy the rest of the application files to Docker, i.e., app.js
:
COPY . .
Expose the port the application will run on:
EXPOSE 4000
Define the command to run the application. This is the same command that Node.js runs on when creating the application locally:
CMD = ["npm","run", "start"]
Your complete code in the Dockerfile should be as shown below:
FROM node:19
WORKDIR /usr/src/app
COPY package*.json .
RUN npm install
COPY . .
EXPOSE 4000
CMD = ["npm","run", "start"]
If you are using a different application, ensure your
Dockerfile
reflects the instruction needed to dockerize your application.
Build the Docker image to check if these instructions work correctly on Docker:
docker build . --tag node_app
And run the application on docker:
docker run -it -p 4000:4000 node_app
The results should remain the same as when you tested the application locally. Otherwise, recheck the above steps and ensure that you entered the code correctly.
Creating a Github Repository and Pushing the Application to Github
GitHub actions require you to host your application code on a remote repository, including the Dockerfile
containing the Docker commands. A GitHub repository store and manage your code and related files. Teams can push changes to this repository to update the application codebase collaboratively.
Create a GitHub repository to add all your code.
Once you create the repository, add your application files by uploading them using Git commands or GitHub Desktop.
For simplicity, I recommend using a Git client with a graphical user interface (GUI), such as GitHub Desktop, Sourcetree, or GitKraken, to make working with Git repositories on GitHub easier. Here’s a brief overview of how to use GitHub Desktop to push code changes to your remote GitHub repository:
- Open GitHub Desktop and log in using your GitHub account
- Select the repository you have created on your GitHub page and open it with GithHub Desktop:

- Clone the repository:

- Add your application code in the local cloned repository
- Review the changes on GitHub Desktop, add a commit message summarizing your changes and click the Commit button:

- Click the Publish button to push your code to the remote GitHub repository:

If you get stuck, check this guide and learn more details on how to use GitHub Desktop.
Setting Up DockerHub
The GitHub action that you will set up will push the application image to the DockerHub repository. Therefore, you must have the correct access token for GitHub Actions to access your DockerHub account. On your DockerHub, you will create an access key for GitHub to connect to your DockerHub account. You can create a new access key as follows:
Navigate to your account setting, security and click on the New Access Token:

Specify the access token description and permission:

Copy the access token:

Once the key is ready, it should be copied immediately and saved securely because it will be provided only once and cannot be retrieved, and it will not be stored on DockerHub. The key will be required for the upcoming steps.
Creating a GitHub Actions Workflow
With all the application code available remotely, you can create a GitHub Actions workflow that will automate the build process and save the build artifacts to DockerHub.
Connecting GitHub Actions With DockerHub
To set up a workflow, GitHub must communicate with DockerHub using the DockerHub access token (that you just created) and your DockerHub username. Your DockerHub access token is a confidential information, and you need to add it as a secret environment variable on GitHub. The workflows will execute this access token and username as secret environment variables on GitHub.
To add the secret environment variables, navigate to the repository you created on Github
In your repository, navigate to Settings, Secrets and Variables, and Actions sections as follows:

Click on the New repository secret button:

Create a DOCKERHUB_USERNAME
variable and add your DockeHub username as the value:

Create a DOCKERHUB_TOKEN
variable and add the access token you created earlier as the value. You should have the following results:

Implementing the GitHub Actions Workflow
GitHub Actions workflow includes creating the processes that trigger events, jobs, and steps that set up a workflow. The docker image will be built and deployed to the DockerHub when you push the code to the main branch of the GitHub repository. Therefore, the GitHub Actions workflow needs to initiate a push event on the main branch of the Github repository you hosted your application code.
To setup the workflow, you need to create the workflow file. You can create one as shown below:
Click on the Actions` tab in your repository and click the set up a workflow yourself:

On the resulting editor, add the following workflow to the main.yml
file as follows:
Define the build trigger:
name: node_app
on: # specify the build to trigger the automated ci/cd
push:
branches:
- "main"
GitHub Actions will name this configuration node_app
. The code specifies the trigger as a push event (changes) to your application code’s main
branch on the GitHub repository you created.
Define the job that indicates the steps to checkout the code and build the Docker images:
jobs:
build:
name: Build Docker image
runs-on: ubuntu-latest # specify the build machine
steps:
GitHub Actions uses jobs
to define one or more tasks that your CI/CD pipeline will run. In the code above, the build
specifies the steps and it also specifies the machine type that the steps will run on using the runs-on
keyword. The value of ubuntu-latest
indicates an ubuntu machine as the pipeline environment. The steps
will define a list of stages to be executed in sequence to fulfill the pipeline objectives. This steps are as follows:
Define the steps that will checkout the code:
- # checkout to the repository on the build machine
name: Checkout
uses: actions/checkout@v3
The uses: actions/checkout@v3
syntax clones your Github repository to the ubuntu-latest
build machine. This will make the code available to the subsequent steps in executing the job.
Define the step to sign in to DockerHub with the credentials in the GitHub Action environment variable secrets:
- # login to Docker Hub using the secrets provided
name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: $
password: $
The uses: docker/login-action@v2
syntax allows GitHub Action to log in to your DockerHub registry based on the DOCKERHUB_USERNAME
and DOCKERHUB_TOKEN
you added as GitHub Actions environment variables.
Define the step that setup the Docker Buildx:
- # create a build kit builder instance
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
The uses: docker/setup-buildx-action@v2
syntax creates a Docker Buildx builder instance. It uses a docker buildx
command that builds Docker images to your desired architectures.
Define the step that build and push the docker image to DockerHub. The workflow will build the image based on the Dockerfile
commands and tag the images. It will finally push and deploy the built image to your DockerHub as follows:
- # build the container image and push it to Docker Hub with \
# the name clockbox.
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
push: true
tags: $/clockbox:latest
The uses: docker/build-push-action@v4
syntax will execute your Dockerfile, push the resulting image to your DockerHub registry, and tag it as $/clockbox:latest
.
The whole workflow code is as shown below:
name: node_app
on: # specify the build to trigger the automated ci/cd
push:
branches:
- "main"
jobs:
build:
name: Build Docker image
runs-on: ubuntu-latest # specify the build machine
steps:
- # checkout to the repository on the build machine
name: Checkout
uses: actions/checkout@v3
- # login to Docker Hub using the secrets provided
name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: $
password: $
- # create a build kit builder instance
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- # build the container image and push it to Docker \
# Hub with the name clockbox.
name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
push: true
tags: $/clockbox:latest
Testing the Builds with GitHub Actions
To deploy the workflow, click on Start commit, add a commit message, and submit:

Navigate to the Actions
tab to view the job and the build status.

After committing from the previous step, the build should start automatically:

Once The GitHub workflow executes all the steps, the build will be completed as follows:

From your DockerHub dashboard, you should be able to view the image that has just been deployed:

Conclusion
GitHub Actions and DockerHub integration streamline your development process. Automating your builds and deployments pipelines creates team efficiency while reducing errors and increasing productivity.
This article has shown you how to create an automated CI/CD build with GitHub and DockerHub. In this article, you have learned:
- The concept of automated CI/CD builds using GitHub Actions and DockerHub.
- How to dockerize an application with Docker
- How to create and deploy an application to GitHub
- How to automatically build and push a Docker image DockerHub with GitHub action.
The code used in this tutorial can be found in this GitHub repository
Earthly makes CI/CD super simple
Fast, repeatable CI/CD with an instantly familiar syntax – like Dockerfile and Makefile had a baby.