This project showcases a production-grade DevOps workflow where I designed, built, containerized, automated, and deployed a Node.js microservice on AWS using modern cloud and DevOps practices.
I built a Node.js-based user microservice and containerized it using Docker to ensure consistency across environments and simplify deployment.
To automate the software delivery process, I implemented a CI/CD pipeline using GitHub Actions. This pipeline builds and pushes Docker images to Docker Hub on every commit, enabling continuous integration and faster delivery.
Using Terraform, I provisioned the required AWS infrastructure, including a VPC, ECS Fargate cluster, security groups, and an Application Load Balancer (ALB). This setup ensures scalability, high availability, and proper traffic routing.
The application was deployed on ECS Fargate (serverless containers), eliminating the need for server management while maintaining reliability and scalability. Incoming traffic is routed through the ALB to the ECS service, making the application publicly accessible.
This project reflects my ability to design and implement end-to-end DevOps solutions, combining infrastructure automation, containerization, and cloud deployment in a real-world scenario.
Client (Browser)
↓
Application Load Balancer (ALB)
↓
Target Group
↓
ECS Service (Fargate)
↓
Task Definition
↓
Docker Container (Node.js App from Docker Hub)
- Node.js (Express)
- Docker
- Docker Hub
- Terraform
- GitHub Actions (CI/CD)
- AWS ECS (Fargate)
- AWS Application Load Balancer (ALB)
- Git & GitHub
user-microservice/ ├── app/
│ ├── app.js
│ ├── package.json
│ └── Dockerfile
├── .github/
│ └── workflows/
│ └── cicd.yml
├── terraform/
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ └── terraform.tfvars
└── screenshots/
mkdir user-microservice
cd user-microservice
mkdir app terraform .github/workflows cd app
npm init -y
npm install expressCreate app.js:
const express = require('express');
const app = express();
const PORT = 3000;
app.get('/', (req, res) => {
res.send('User Microservice Running ');
});
app.get('/api/users', (req, res) => {
res.json([
{ id: 1, name: "Chidera" },
{ id: 2, name: "Pam" }
]);
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});Run locally:
node app.jsCreate Dockerfile:
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]Build and push image:
docker build -t yourdockerhubusername/user-microservice .
docker login
docker push yourdockerhubusername/user-microservice- Create Workflow Directory
Inside your project root:
mkdir -p .github/workflows
cd .github/workflows
touch cicd.yml- Add GitHub Actions Workflow
Create a file:
Create .github/workflows/cicd.yml:
Paste the following configuration:
name: CI/CD Pipeline
on:
push:
branches:
- "main"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: npm install
- name: Run tests (optional)
run: npm test || echo "No tests yet"
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Build Docker image
run: docker build -t ${{ secrets.DOCKER_HUB_USERNAME }}/user_microservice:latest .
- name: Push Docker image
run: docker push ${{ secrets.DOCKER_HUB_USERNAME }}/user_microservice:latestConfigure GitHub Secrets
Go to your GitHub repository:
Settings → Secrets and variables → Actions → New repository secret
Add:
| Secret Name | Value |
|---|---|
| DOCKER_HUB_USERNAME | Your Docker Hub username |
| DOCKER_HUB_ACCESS_TOKEN | Your Docker Hub password |
- Trigger the Pipeline
Commit and push your code:
git add .
git commit -m "Add CI/CD pipeline"
git push origin main- Verify Pipeline Execution
Go to GitHub → Actions tab
Click on the workflow run
Confirm:
✅ Build successful
✅ Docker image pushed to Docker Hub
Navigate to Terraform folder:
cd ../terraformInitialize Terraform:
terraform initDeploy infrastructure:
terraform applyAfter deployment, Terraform outputs:
alb_dns = "your-alb-dns-name"Open in browser:
http://<alb_dns>
After confirming the application is working, destroy resources to avoid unnecessary AWS charges:
terraform destroyType yes when prompted.
-
Used
.gitignoreto exclude:- Terraform state files
.envfilesterraform.tfvars
-
Used
.dockerignoreto prevent unnecessary or sensitive files from being included in Docker images, reducing image size and improving security -
Used environment variables and GitHub secrets
-
Avoided hardcoding credentials
1. ECS + ALB Integration
- Challenge: Understanding flow
- Solution: Broke into ALB → Target Group → ECS
2. CI/CD Pipeline Setup
- Challenge: Docker authentication
- Solution: Used GitHub Secrets
3. App Not Accessible
- Challenge: No browser access
- Solution: Fixed port + security group + ALB listener
- CI/CD improves deployment efficiency
- Terraform enables repeatable infrastructure
- ECS Fargate removes server management overhead
- ALB is essential for production traffic routing
Prerequisites
- AWS Account
- Terraform installed
- Docker installed
- AWS CLI configured (aws configure)
- Docker Hub account
- Git installed
Step 1: Clone Repository
git clone https://github.com/your-username/user-microservice.git
cd user-microserviceStep 2: Run Application Locally
cd app
npm install
node app.jsTest in browser:
http://localhost:3000/api/usersStep 3: Build & Push Docker Image (Manual First Time)
docker build -t yourdockerhubusername/user-microservice ./app
docker push yourdockerhubusername/user-microserviceStep 4: Configure Terraform Variables
terraform/terraform.tfvarsExample
aws_region = "us-east-1"
docker_image = "yourdockerhubusername/user-microservice"
vpc_id = "your-vpc-id"
subnets = ["subnet-1", "subnet-2"]Step 5: Deploy Infrastructure
cd terraform
terraform init
terraform plan
terraform applyType
yesStep 6: Access Application
After deployment:
Copy ALB DNS from Terraform output
Open in browser:
http://<ALB-DNS>Step 7: Test CI/CD Automation
Make a change in your app:
git add .
git commit -m "Update app"
git push origin mainNote: This triggers:
- GitHub Actions builds new image
- Pushes to Docker Hub
Step 8: Update Deployment
To reflect new changes:
terraform apply(or update ECS service to pull latest image)
Step 9: Destroy Infrastructure (VERY IMPORTANT)
To avoid AWS charges:
terraform destroyType:
yes- Add HTTPS with ACM
- Implement autoscaling
- Use Terraform remote backend (S3 + DynamoDB)
- Add monitoring (CloudWatch)
This project showcases my ability to:
- Build and containerize applications
- Automate deployments using CI/CD
- Provision infrastructure using Terraform
- Deploy scalable applications on AWS
It reflects my growing expertise in DevOps and my commitment to building production-ready systems.














