Zero-Touch Deployment: My CI/CD Journey with Node.js, Docker, and Jenkins
You need to have basic knowledge of AWS, Docker, Jenkins before starting this project.

I am a third year B.Tech graduate from VIT Bhopal University. I love to code in Java and and skilling up myself in the field of DevOps.
Continuous Integration and Continuous Deployment (CI/CD) are essential practices in modern software development that help deliver reliable code rapidly. In my recent project, I automated the entire lifecycle of a Node.js web app β from source code to deployment β using Docker, Jenkins, and AWS EC2.
In this post, Iβll walk you through the setup and architecture of my CI/CD pipeline, the Docker containerization process, and the deployment strategy on a free-tier AWS EC2 instance.
Project Overview
The project is a Node.js application available on GitHub: Code-Whispers. The app runs on port 8001 and serves its main API at /api/v2/url.
My goal was to automate the process of building, packaging, and deploying this app with zero manual intervention.
Pre-task: Containerizing the Node.js App with Docker
Before diving into deployment, I designed a Dockerfile to containerize the app and its dependencies:
# Use an official Node.js runtime as a parent image
FROM node:18-slim
# Set the working directory in the container
WORKDIR /usr/src/app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install project dependencies
RUN npm install
# Bundle app source
COPY . .
# Make port 8001 available to the world outside this container
EXPOSE 8001
# Define environment variable for the port
ENV PORT=8001
# Run index.js when the container launches
CMD [ "node", "index.js" ]
Docker binds the application and its dependencies into a single container that can run anywhere.
Adding Jenkins to Docker group
sudo usermod -aG docker jenkins
Running on AWS EC2 Free Tier
I used a t2.micro EC2 instance (Ubuntu).
Update Ubuntu:
sudo apt updateInstall Docker:
sudo apt install docker.ioInstall Jenkins (refer to the Jenkins install guide).
Open necessary ports in the AWS Security Group.

Login into Jenkins
Navigate to http://<ec2-public-ip>:8080
Register and log in.
Click New Item.
Name your project.
Choose Pipeline as the type.
Paste your Jenkinsfile into the Pipeline configuration.

Building a Jenkins Pipeline
The pipeline is triggered on every push to the main branch using GitHub webhooks.
Pipeline Stages
Clean Up: Removes dangling Docker images to free disk space on the Jenkins agent.
Code Checkout: Clones the repository from GitHub.
Build and Push:
Uses Jenkins credentials to securely access Docker Hub and an
.envfile.Builds the Docker image tagged as
username/code-whispers-app:latest.Pushes the image to Docker Hub.
Deploy:
Removes any existing container on the AWS EC2 instance.
Pulls the latest image from Docker Hub.
Runs the container with environment variables loaded from the
.envfile, exposing port 8001.
Jenkinsfile excerpt:
pipeline {
agent any
stages {
stage('Clean Up') {
steps {
script {
// Remove dangling images (untagged)
sh 'docker image prune -f'
}
}
}
stage("Code") {
steps {
git url: "https://github.com/CoolSrj06/code-whispers.git", branch: "main"
}
}
stage("Build and Push") {
steps {
withCredentials([
usernamePassword(credentialsId: "dockerHub", usernameVariable: "dockerHubUser", passwordVariable: "dockerHubPass"),
file(credentialsId: "ENV_FILE_ID", variable: "ENV_FILE")
]) {
script {
def dockerImage = "${dockerHubUser}/code-whispers-app:latest"
sh 'rm -f .env'
// Copy the .env file into the workspace
sh '''cp $ENV_FILE .env'''
//sh 'cat .env' // Optional: for debugging (remove in production!)
// Login to Docker Hub
sh '''
echo "$dockerHubPass" | docker login -u "$dockerHubUser" --password-stdin
'''
// Build and push Docker image
sh "docker build . -t ${dockerImage}"
sh "docker push ${dockerImage}"
}
}
}
}
stage("Deploy") {
steps {
withCredentials([
usernamePassword(credentialsId: "dockerHub", usernameVariable: "dockerHubUser", passwordVariable: "dockerHubPass"),
file(credentialsId: "ENV_FILE_ID", variable: "ENV_FILE")
]) {
script {
def dockerImage = "${dockerHubUser}/code-whispers-app:latest"
// Copy .env file again
sh 'rm -f .env'
sh 'cp $ENV_FILE .env'
// Optional: Login to Docker Hub again if needed
sh '''
echo "$dockerHubPass" | docker login -u "$dockerHubUser" --password-stdin
'''
// Remove existing container (if running)
sh "docker rm -f code-whispers-app || true"
// Pull the latest image
sh "docker pull ${dockerImage}"
// Run the container
sh "docker run -d --env-file .env -p 8001:8001 --name code-whispers-app ${dockerImage}"
}
}
}
}
}
post {
always {
sh 'docker logout'
cleanWs()
}
}
}
π Secure Handling of Credentials
Docker Hub username and password are stored as Jenkins credentials.
The
.envfile with environment variables is securely injected via Jenkins credentials binding.The pipeline ensures these sensitive files are never exposed in logs or the workspace permanently.
Pro Tip: Always use Jenkins credentials for security best practices.
I am also sharing my configuration setting.



Once all the Pipeline configuration is set, you can click Build Now.

Monitor the build logs and navigate to: http://<ec2-public-ip>:8001/api/v2/url/ to see your app in action.

Benefits and Learnings
Automation: One push to GitHub triggers a full build, test (planned), push, and deploy cycle.
Repeatability: Identical environments from developer machine to production.
Efficiency: Clean-up step keeps Jenkins agent healthy.
Security: Credentials and environment variables are managed securely.
Challenges: Handling .env securely and ensuring zero downtime deployment on EC2 were tricky but resolved with the current approach.
Try It Yourself
Check out the code here: https://github.com/CoolSrj06/code-whispers.git
Feel free to clone, build, and customize the pipeline for your own projects!
Conclusion
Implementing CI/CD with Docker and Jenkins on AWS EC2 is a game-changer for automating deployments. It reduces manual work, improves reliability, and sets the stage for continuous improvement.
Have questions or feedback? Letβs connect in the comments! π