“Automation is the key to speed and reliability in modern software development.”
Table of Contents
- Introduction
- Architecture Overview
- Prerequisites
- CI/CD Workflow (Step-by-Step)
- GitLab Pipeline Configuration
- Deployment Process on AWS EC2
- Security Best Practices
- Interesting Facts & Statistics
- FAQs
- Key Takeaways
- Conclusion
1. Introduction
Continuous Integration and Continuous Deployment (CI/CD) is a modern development practice that automates the process of building, testing, and deploying applications. This document explains how to set up a CI/CD pipeline for a Node.js application using GitLab CI/CD and deploy it automatically to an AWS EC2 instance.
The goal is to:
- Automate deployment
- Reduce manual errors
- Improve development speed
- Ensure reliable releases
2. Architecture Overview
- Backend: Node.js
- Version Control: GitLab
- CI/CD Tool: GitLab Pipeline
- Server: AWS EC2 (Ubuntu)
- Process Manager: PM2
- SSH Authentication: Secure Key-based login
High-level Flow:
- Developer pushes code to GitLab repository.
- GitLab pipeline triggers automatically.
- Pipeline installs dependencies and builds project.
- GitLab connects to EC2 via SSH.
- Code is pulled on EC2 server.
- Application restarts using PM2.
- Nginx routes HTTP traffic to the Node.js app
3. Prerequisites
Before setting up CI/CD, ensure the following:
GitLab Setup
- GitLab repository created
- Branches (dev/stage/prod) configured
- GitLab Runner enabled (shared runner works)
AWS EC2 Setup
- Ubuntu EC2 instance running
- Node.js & npm installed
- Git installed on server
- SSH access configured PM2 installed globally
npm install pm2 -g
SSH Key Setup
- Generate SSH key on local system:
ssh-keygen -t rsa -b 4096
- Add public key to EC2:
~/.ssh/authorized_keys
- Add private key in GitLab:
**GitLab → Settings → CI/CD → Variables**
- SSH_PRIVATE_KEY
- SSH_HOST
- SSH_USER
4. CI/CD Workflow (Step-by-Step)
Step 1: Developer Pushes Code
Developer pushes code to the GitLab branch (e.g., staging or production).
Step 2: Pipeline Triggered
GitLab detects changes and starts pipeline.
Step 3: Install Dependencies
Pipeline installs Node.js packages.
Step 4: SSH Connection
GitLab pipeline connects to AWS EC2 via SSH.
Step 5: Deployment
On EC2 server:
Pull latest code
Install dependencies
Restart Node app with PM2
Step 6: Live Deployment
Application updated automatically without manual login.
“CI/CD turns deployment from a risky event into a routine process.”
5. GitLab Pipeline Configuration
Create .gitlab-ci.yml in project root:
stages:
- production
deploy_to_ec2:
stage: production
image: alpine:latest
only:
- prd before_script:
- apk add --no-cache openssh
- mkdir -p ~/.ssh
- cp "$SSH_PRIVATE_KEY" ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh-keyscan -H "$SSH_HOST" >> ~/.ssh/known_hosts
script:
- |
ssh "$SSH_USER@$SSH_HOST" << 'EOF'
set -e
echo "---------- Checking Directory ---------------"
cd "$PATH_DIR"
echo "---------------- Load NVM ----------------"
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"
echo "--------Node Version:-----------"
node -v || echo "Node not found"
echo "----------NPM Version:--------------"
npm -v || echo "npm not found"
echo "----------------- Git Pull ------------------"
git pull origin "$PRD_BRANCH"
echo "----------------- npm install ----------------"
npm install
echo "----------------- Restart PM2 ----------------"
pm2 restart all
echo "---------------- Deployment Completed ----------------"
EOF
6. Deployment Process on AWS EC2
On EC2 server:
- Clone project first time:
- git clone
- cd project
- npm install
- pm2 start app.js --name node-app
After CI/CD setup:
Deployment becomes automatic on every push.
Practical Demonstration (Images Explained)
Step 1. BEFORE: Original Login Page
- This shows the original application running at https://poc-addweb-app.addwebprojects.com with the title "Login Page". This is the state before making any code changes.
Step 2. Making Changes & Pushing Code
- This terminal screenshot shows the developer workflow.
- The code was reflected in the GitLab repository 6 minutes ago.
Step 3. GitLab Pipelines Dashboard
- This shows the Pipelines page in GitLab with successful deployments:
The highlighted row #2330658746 is the most recent deployment that was triggered automatically when code was pushed to the prd branch.
The pipeline deployment was successful. See the image below.
Step 4. AFTER: Updated Login Page
This shows the application after successful deployment. The title has changed from "Login Page" to "Addweb Login Page" - confirming the CI/CD pipeline worked correctly!
GitLab Pipeline Failure Scenario
Step 1. Intentionally Introduce an Error
To test pipeline failure behavior, we intentionally modified the package.json file by adding an invalid dependency:
Example change in package.json:
- "package-does-not-exist-3232": "2.1.0"
This package does not exist in the npm registry. the purpose was to simulate a real-world mistake such as:
- Typo in package name
- Incorrect dependency version
- Invalid module added by mistake
Step 2. Commit and Push the Wrong Code
After modifying package.json, the changes were committed and pushed to the prd branch:
- git commit -m "We mentioned the wrong package name in package.json."
- git push origin prd
Since our GitLab Pipeline is configured to run automatically on the prd branch, this push immediately triggered a new pipeline execution.
Step 3. GitLab Pipeline Triggered Automatically
As expected, GitLab Pipelines started running automatically as soon as the code was pushed.
In the Pipelines dashboard we can see:
- New pipeline execution created #2330702561
- Status initially shown as “Failed”
This confirms that the CI/CD automation is working correctly.
Step 4. Pipeline Execution Failed
During pipeline execution, the following command was executed on the server:
- npm install **
Because we added a non-existent package, the installation failed with this error:
npm error 404 Not Found - GET https://registry.npmjs.org/package-does-not-exist-3232 - Not found
npm error 404 The requested resource **'package-does-not-exist-3232@2.1.0' could not be found or you do not have permission to access it.
npm error 404
As a result:
- The pipeline step stopped
- Deployment process was aborted
- Application was NOT restarted
- Previous working version remained intact
If any step in the CI/CD pipeline fails, the deployment automatically stops. This protects production from broken or unstable code.
“First automate, then optimize.”
7. Security Best Practices
- Use SSH keys instead of passwords
- Restrict EC2 security group (only required ports)
- Store secrets in GitLab CI/CD variables
- Disable root login on EC2
- Use environment variables for API keys
- Enable firewall (UFW)
8. Interesting Facts & Statistics
- Companies using CI/CD deploy 30x faster than manual deployment.
- Automated pipelines reduce deployment failures by 40–60%.
- GitLab CI/CD supports Auto DevOps for full automation.
- AWS EC2 is used by millions of applications worldwide.
- 90% of DevOps teams use CI/CD pipelines in production.
9. FAQs
Q1: Why use CI/CD for Node.js?
→ It automates testing and deployment, saving time and reducing errors.
Q2: Why GitLab CI/CD?
→ GitLab provides built-in CI/CD with repositories, making setup easier.
Q3: Why use PM2?
→ PM2 keeps Node.js apps running and supports auto restart.
Q4: Can we deploy multiple environments?
→ Yes, create separate branches:
- dev
- staging
- production Q5: Is EC2 safe for production? → Yes, if proper security (SSH keys, firewall, updates) is applied.
10. Key Takeaways
- CI/CD automates build and deployment.
- GitLab pipeline integrates easily with AWS EC2.
- SSH keys ensure secure deployment.
- PM2 manages Node.js processes efficiently.
- Automated deployment saves time and reduces downtime.
11. Conclusion
Implementing CI/CD for a Node.js application using GitLab and AWS EC2 significantly improves development workflow and deployment reliability.
With automated pipelines:
- Developers can focus on coding
- Deployments become faster
- Errors are minimized
- Applications stay updated continuously
CI/CD is no longer optional, it is a standard practice for modern scalable applications.
About the Author: Rajan is a DevOps Engineer at AddWebSolution, specializing in automation infrastructure, Optimize the CI/CD Pipelines and ensuring seamless deployments.










Top comments (0)