DEV Community

Omkar Sharma
Omkar Sharma

Posted on

Jenkins CI/CD Pipeline for a Dockerized Node.js Application: Manual Trigger vs Automatic Trigger Using GitHub Webhooks

Have you ever pushed code to GitHub and wished your application could automatically build and deploy itself without logging into a server or clicking a button in Jenkins? In this article, you'll learn how to build a complete CI/CD pipeline for a Dockerized Node.js application using Jenkins, starting with manual deployments and progressing to fully automated deployments using GitHub webhooks.

We will cover:

  • Creating a Jenkins pipeline
  • Building a Docker image
  • Deploying a container
  • Triggering builds manually
  • Triggering builds automatically
  • GitHub Personal Access Tokens
  • Fine-grained vs Classic Tokens
  • Jenkins credentials
  • GitHub webhooks
  • Required Jenkins plugins
  • Common errors and troubleshooting The goal is to understand not only how to configure everything but also why each component is needed.

Node.js Application Repo URL- https://github.com/omkarsharma2821/Node.js-App-Deploy-Github-Action

Architecture Overview

The complete flow looks like this:

Developer
    |
    | Git Push
    v
GitHub Repository
    |
    | Webhook
    v
Jenkins
    |
    | Build Docker Image
    v
Docker
    |
    | Run Container
    v
Application Running
Enter fullscreen mode Exit fullscreen mode

Without webhooks:

Developer
    |
    | Git Push
    v
GitHub Repository

Jenkins Build Now (Manual Trigger)
    |
    v
Build and Deploy
Enter fullscreen mode Exit fullscreen mode

With webhooks:

Developer
    |
    | Git Push
    v
GitHub Repository
    |
    v
Webhook
    |
    v
Jenkins
    |
    v
Build and Deploy Automatically
Enter fullscreen mode Exit fullscreen mode

Prerequisites

Before starting, ensure you have:

  • Ubuntu Server
  • Jenkins installed
  • Docker installed
  • Git installed
  • GitHub repository
  • Node.js application with Dockerfile Verify installations:
jenkins --version
docker --version
git --version
Enter fullscreen mode Exit fullscreen mode

Installing Docker on Jenkins Server

Install Docker:

sudo apt update
sudo apt install docker.io -y
Enter fullscreen mode Exit fullscreen mode

Enable Docker:

sudo systemctl enable docker
sudo systemctl start docker
Enter fullscreen mode Exit fullscreen mode

Verify:

docker --version
Enter fullscreen mode Exit fullscreen mode

Allow Jenkins to Use Docker

By default Jenkins cannot execute Docker commands.

Add Jenkins user to Docker group:

sudo usermod -aG docker jenkins
Enter fullscreen mode Exit fullscreen mode

Restart Jenkins:

sudo systemctl restart jenkins
Enter fullscreen mode Exit fullscreen mode

Verify:

sudo su - jenkins
docker ps
Enter fullscreen mode Exit fullscreen mode

If Docker works without sudo, Jenkins is ready.

Creating the Pipeline

Initially we created a Jenkins pipeline that manually clones the repository.

Example:

pipeline {
    agent any

    stages {

        stage('Clone Repository') {
            steps {
                sh '''
                    mkdir -p devops
                    cd devops
                    rm -rf Node.js-App-Deploy-Github-Action
                    git clone -b main https://github.com/username/repository.git
                '''
            }
        }

        stage('Build Image') {
            steps {
                sh '''
                    cd devops/Node.js-App-Deploy-Github-Action
                    docker build -t node-app .
                '''
            }
        }

        stage('Deploy') {
            steps {
                sh '''
                    docker run -d -p 8000:8080 node-app
                '''
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This works, but every deployment requires manually clicking:

Build Now
Enter fullscreen mode Exit fullscreen mode

Problem with Multiple Deployments

Suppose the application is already running.

Running:

docker run -d -p 8000:8080 node-app
Enter fullscreen mode Exit fullscreen mode

again will fail because port 8000 is already occupied.

Error:

Bind for 0.0.0.0:8000 failed
Enter fullscreen mode Exit fullscreen mode

Better Deployment Approach

Before starting a new container, remove the old one.

docker rm -f node-app-container || true
Enter fullscreen mode Exit fullscreen mode

Then start a new container:

docker run -d --name node-app-container -p 8000:8080 node-app
Enter fullscreen mode Exit fullscreen mode

Understanding docker rm -f node-app-container || true

Let's break it down.

docker rm

Removes a container.

docker rm node-app-container
Enter fullscreen mode Exit fullscreen mode

Works only if container is stopped.

-f

Force remove.

docker rm -f node-app-container
Enter fullscreen mode Exit fullscreen mode

This:

  1. Stops container
  2. Removes container

||

OR operator.

Syntax:

command1 || command2
Enter fullscreen mode Exit fullscreen mode

If command1 fails, command2 executes.

true

Always returns success.

true
Enter fullscreen mode Exit fullscreen mode

Exit code:

0
Enter fullscreen mode Exit fullscreen mode

Final Meaning

docker rm -f node-app-container || true
Enter fullscreen mode Exit fullscreen mode

If container exists:

Remove it
Enter fullscreen mode Exit fullscreen mode

If container doesn't exist:

Ignore error and continue
Enter fullscreen mode Exit fullscreen mode

This prevents Jenkins from failing.

Manual Triggering

The simplest approach is manual execution.

Navigate to:

Jenkins Job
|
└── Build Now
Enter fullscreen mode Exit fullscreen mode

Advantages:

  • Easy to understand
  • Good for learning
    Disadvantages:

  • Requires human intervention

  • Not real CI/CD

Automatic Triggering

The goal of CI/CD is:

Code Push
    |
    v
Automatic Build
    |
    v
Automatic Deployment
Enter fullscreen mode Exit fullscreen mode

This is where GitHub webhooks come into play.

Required Jenkins Plugins

Install the following Jenkins plugins before configuring the CI/CD pipeline:

1. Git Plugin

  • Enables Jenkins to interact with Git repositories.
  • Allows Jenkins to clone repositories, fetch changes, and checkout specific branches.
  • Required for integrating Jenkins with GitHub repositories.

2. GitHub Plugin

  • Provides integration between Jenkins and GitHub.
  • Allows Jenkins to communicate with GitHub repositories and services.
  • Supports GitHub-related features within Jenkins.

3. GitHub Integration Plugin

  • Enables GitHub webhook support.
  • Allows Jenkins to automatically trigger builds when code is pushed to GitHub.
  • Essential for implementing automated CI/CD workflows.

4. Pipeline Plugin

  • Enables support for Jenkins Pipelines.
  • Allows execution of Jenkinsfiles written in Declarative or Scripted Pipeline syntax.
  • Required for defining CI/CD workflows as code.

5. Credentials Plugin

  • Provides secure storage for sensitive information.
  • Allows storing:
    • GitHub Personal Access Tokens (PATs)
    • Usernames and passwords
    • SSH keys
    • API tokens
  • Prevents hardcoding secrets in Jenkins jobs and pipelines.

GitHub Authentication

When Jenkins needs to access a GitHub repository, authentication requirements depend on the repository type.

Public Repository

  • Can typically be cloned without authentication.

Example:

git clone https://github.com/username/repository.git
Enter fullscreen mode Exit fullscreen mode

Private Repository

  • Requires authentication.
  • GitHub no longer supports account passwords for Git operations.
  • A Personal Access Token (PAT) must be used instead of a password.

Why Use a Personal Access Token (PAT)?

  • More secure than passwords.
  • Allows granular permission control.
  • Can be revoked without affecting your GitHub account password.
  • Recommended by GitHub for all Git operations requiring authentication.

Classic Personal Access Token

Older token type.

Advantages:

  • Simple
  • Easy to configure
    Disadvantages:

  • Broad permissions

  • Less secure
    Example scopes:

repo
workflow
admin:repo_hook
Enter fullscreen mode Exit fullscreen mode

Fine-Grained Personal Access Token

Newer and recommended approach.

Advantages:

  • Repository-level access
  • Better security
  • Granular permissions Example:
Repository Access:
Only selected repositories
Enter fullscreen mode Exit fullscreen mode

Permissions:

Contents: Read and Write
Metadata: Read
Webhooks: Read and Write
Enter fullscreen mode Exit fullscreen mode

Fine-Grained vs Classic Token

Feature Fine-Grained Classic
Security High Lower
Repository Scope Specific Broad
Permission Control Granular Broad
Recommended Yes Legacy

For modern projects, prefer Fine-Grained tokens.

Adding GitHub Token to Jenkins

Navigate to:

Manage Jenkins
|
Credentials
Enter fullscreen mode Exit fullscreen mode

Select:

Global Credentials
Enter fullscreen mode Exit fullscreen mode

Choose:

Add Credentials
Enter fullscreen mode Exit fullscreen mode

Kind:

Username with Password
Enter fullscreen mode Exit fullscreen mode

Example:

Username: GitHub Username
Password: Personal Access Token
Enter fullscreen mode Exit fullscreen mode

ID:

github-creds
Enter fullscreen mode Exit fullscreen mode

Save.

Pipeline Script vs Pipeline Script from SCM

Many beginners get confused here.

Pipeline Script

Pipeline stored inside Jenkins UI.

Example:

pipeline {
    agent any
}
Enter fullscreen mode Exit fullscreen mode

Advantages:

  • Quick setup
    Disadvantages:

  • Not version controlled

  • Difficult to maintain

Pipeline Script from SCM

Pipeline stored in GitHub repository.

Repository structure:

project/
|
|-- Dockerfile
|-- package.json
|-- app.js
|-- Jenkinsfile
Enter fullscreen mode Exit fullscreen mode

Jenkins automatically downloads Jenkinsfile.

Advantages:

  • Version controlled
  • Industry standard
  • Easier maintenance Recommended approach.

Configuring Pipeline from SCM

Create Jenkins job.

Select:

Pipeline
Enter fullscreen mode Exit fullscreen mode

Under Definition:

Pipeline script from SCM
Enter fullscreen mode Exit fullscreen mode

SCM:

Git
Enter fullscreen mode Exit fullscreen mode

Repository URL:

https://github.com/username/repository.git
Enter fullscreen mode Exit fullscreen mode

Branch:

*/main
Enter fullscreen mode Exit fullscreen mode

Script Path:

Jenkinsfile
Enter fullscreen mode Exit fullscreen mode

Save.

Creating GitHub Webhook

Navigate to:

GitHub Repository
|
Settings
|
Webhooks
|
Add Webhook
Enter fullscreen mode Exit fullscreen mode

Payload URL:

http://JENKINS_PUBLIC_IP:8080/github-webhook/
Enter fullscreen mode Exit fullscreen mode

Content Type:

application/json
Enter fullscreen mode Exit fullscreen mode

Event:

Just the push event
Enter fullscreen mode Exit fullscreen mode

Save webhook.

Configuring Jenkins Trigger

Open job configuration.

Under Build Triggers:

Select:

GitHub hook trigger for GITScm polling
Enter fullscreen mode Exit fullscreen mode

Save.

Testing the Webhook

Push code:

git add .
git commit -m "testing webhook"
git push origin main
Enter fullscreen mode Exit fullscreen mode

Expected flow:

GitHub Push
    |
    v
Webhook
    |
    v
Jenkins
    |
    v
Pipeline Starts
Enter fullscreen mode Exit fullscreen mode

No manual click required.


Common Troubleshooting

Webhook Returns 404

Cause:

Wrong webhook URL
Enter fullscreen mode Exit fullscreen mode

Correct:

http://SERVER-IP:8080/github-webhook/
Enter fullscreen mode Exit fullscreen mode

Webhook Returns 403

Cause:

Authentication or security issue
Enter fullscreen mode Exit fullscreen mode

Verify:

  • GitHub plugin
  • GitHub integration plugin

Webhook Returns 200 But Build Doesn't Start

Common cause:

Pipeline Script instead of Pipeline Script from SCM
Enter fullscreen mode Exit fullscreen mode

or

Repository mapping issue
Enter fullscreen mode Exit fullscreen mode

Dockerfile Not Found

Example:

unable to evaluate symlinks in Dockerfile path
Enter fullscreen mode Exit fullscreen mode

Cause:

Wrong working directory.

Check:

pwd
ls -la
Enter fullscreen mode Exit fullscreen mode

Verify Dockerfile location.

Permission Denied While Running Docker

Cause:

Jenkins not in docker group
Enter fullscreen mode Exit fullscreen mode

Fix:

sudo usermod -aG docker jenkins
sudo systemctl restart jenkins
Enter fullscreen mode Exit fullscreen mode

Final Jenkinsfile

pipeline {
    agent any

    stages {

        stage('Build Image') {
            steps {
                sh '''
                    docker build -t node-app .
                '''
            }
        }

        stage('Deploy') {
            steps {
                sh '''
                    docker rm -f node-app-container || true
                    docker run -d --name node-app-container -p 8000:8080 node-app
                '''
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

A Jenkins pipeline can be triggered manually or automatically. Manual triggering is useful for learning and testing, but real CI/CD begins when code pushes automatically trigger builds and deployments.

The recommended production approach is:

  1. Store the Jenkinsfile in GitHub.
  2. Use Pipeline Script from SCM.
  3. Configure GitHub credentials using a Personal Access Token.
  4. Enable GitHub webhook integration.
  5. Use Docker for packaging and deployment.
  6. Remove old containers before deploying new versions. With this setup, every code push automatically builds a Docker image, deploys a fresh container, and updates the application without requiring any manual intervention.

✍️ Author: Omkar Sharma

📬 Feel free to connect on LinkedIn or explore more on GitHub

Top comments (0)