Full-stack Microservice App (React Frontend + Node.js Backend)
With Docker, Docker Compose, Docker Swarm, Jenkins CI/CD, Docker Networking, Parallelism
🛠️ Project Idea (Simple & Clean)
Frontend: React App (shows "Hello from Backend")
Backend: Node.js API (returns "Hello from Backend API")
Goal: Use all DevOps concepts to build, deploy, and orchestrate the app
STEP 1️⃣ — Setup project structure
my-devops-project/
├── backend/
│ ├── Dockerfile
│ ├── server.js
│ └── package.json
├── frontend/
│ ├── Dockerfile
│ └── (React app)
├── docker-compose.yml
└── Jenkinsfile
- First make a main folder i.e my-devops-project.
- Inside, make two sub folders i.e backend and frontend.
- Now, inside backend folder, make Dockerfile and server.js.
- Inside frontend folder, make Dockerfile.(In further steps we will setup react project by commands)
- Outside frontend and backend folder, make Jenkinsfile (Keep J capital) and docker-compose.yml.
- Now follow below steps.
STEP 2️⃣ — Backend: Node.js app
We will run following commands to build a simple backend project setup.
cd backend
npm init -y
npm install express
In server.js, write below code:
const express = require('express');
const app = express();
const PORT = 5000;
app.get('/', (req, res) => {
res.send('Hello from Backend API');
});
app.listen(PORT, () => {
console.log(`Backend running on port ${PORT}`);
});
STEP 3️⃣ — Frontend: React app
npx create-react-app .
Now, in App.js, replace code with below code:
import { useEffect, useState } from 'react';
function App() {
const [message, setMessage] = useState('');
useEffect(() => {
fetch('http://backend:5000')
.then(res => res.text())
.then(data => setMessage(data));
}, []);
return {message};
}
export default App;
STEP 4️⃣ — Dockerize Backend + Frontend
✅ Backend Dockerfile
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5000
CMD ["node", "server.js"]
✅ Frontend Dockerfile
# Stage 1: Build the React app
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Stage 2: Serve the React app with Nginx
FROM nginx:alpine
# Copy the build folder from the first stage to Nginx's default HTML directory
COPY --from=build /app/build /usr/share/nginx/html
# Expose port 80 for the Nginx server
EXPOSE 80
# Start Nginx
CMD ["nginx", "-g", "daemon off;"]
Note: While making frontend using react,angular or any other framework, we prefer to provide build folder to container because it's clean, optimized, small, fast, and production-ready — no unnecessary dev code.
Why we prefer this?
Without build folder | With build folder (preferred) |
---|---|
Heavy container (with Node.js) | Lightweight container (Nginx/alpine) |
Slower load (dev bundle) | Optimized, fast-loading |
Exposes dev dependencies/tools | Only static files exposed |
More resource usage | Less CPU/RAM |
STEP 5️⃣ — Docker Compose (Networking + Storage)
Now, let's write docker-compose.yml file according to docker swarm. Generally, we write build step in compose file, but while embedding docker swarm, we will directly provide image because docker swarm demands image not do building process.
version: '3.8'
services:
backend:
image: aryasingh55/backend:v1
ports:
- "5002:5002"
networks:
- appnet
volumes:
- backend-data:/app
frontend:
image: aryasingh55/frontend:v1
ports:
- "5000:80"
networks:
- appnet
volumes:
- frontend-data:/app
networks:
appnet:
external: true
volumes:
backend-data:
frontend-data:
Note: appnet is custom network name. You can provide any name of your choice.
Note: image name for backend should be : your-dockerhub-username/backend-folder-name:v1
Note: image name for frontend should be : your-dockerhub-username/frontend-folder-name:v1
Note: As reference, I have written my credentials above.
Note: In networks -> appnet -> external , it is true. It means we have created swarm network through CLI command. So, run this command:
docker network create --driver overlay appnet
STEP 6️⃣ — Docker Hub operations
Create Docker Hub account: https://hub.docker.com
- Login Docker CLI using below command:
docker login
- Tag + Push backend image
docker build -t your-custom-name/backend:v1 backend/
docker tag your-custom-name/backend:v1 your-dockerhub-username/backend:v1
docker push your-dockerhub-username/backend:v1
- Tag + Push frontend image
docker build -t your-custom-name/frontend:v1 frontend/
docker tag your-custom-name/frontend:v1 your-dockerhub-username/frontend:v1
docker push your-dockerhub-username/frontend:v1
STEP 7️⃣ — Docker Swarm Cluster
- Open bash, run following commands.
docker swarm init --advertise-addr
# to check wsl-ip-address, run below command.
ip addr
Deploy services using compose (in swarm mode)
docker stack deploy -c docker-compose.yml devopsapp
Check services
docker service ls
docker stack ps devopsapp
docker service logs
STEP 8️⃣ — Jenkins (port 8080 UI)
Make sure, you have setup you jenkins before and it is running on port 8080.
Access Jenkins: http://localhost:8080
Now, you will get asked for login with your username and password.
After successful login, you'll see dashboard. Click new item. Name your project,select pipeline as option and click save.
Now do below configurations.
1️⃣ Jenkins UI → New Item → Pipeline Job
2️⃣ Source Code Mgmt (SCM) → Git repo URL (https://github.com/your-repo-url.git)
3️⃣ Pipeline → Definition: Pipeline script from SCM
→ SCM: Git
→ Repo URL aur branch
Note: No need to do step 3 if you have made repo public on github.
→ Change */master -> */main
→ Script Path = Jenkinsfile
STEP 9️⃣ — Jenkinsfile (CI/CD Pipeline)
📝 Parallelism + Docker build + Push + Deploy
pipeline {
agent any
environment {
DOCKERHUB_CREDENTIALS = 'dockerhub-creds'
IMAGE_BACKEND = 'aryasingh55/backend:v1'
IMAGE_FRONTEND = 'aryasingh55/frontend:v1'
STACK_NAME = 'devopsapp'
COMPOSE_FILE = 'docker-compose.yml'
}
stages {
stage('Clone Repository') {
steps {
git branch: 'main', url: 'https://github.com/Aryagithubk/devops_prac1.git'
}
}
stage('Build docker images (Parallel)') {
parallel {
stage('Build backend') {
steps {
script {
docker.build("${IMAGE_BACKEND}", 'backend/')
}
}
}
stage('Build frontend') {
steps {
script {
docker.build("${IMAGE_FRONTEND}", 'frontend/')
}
}
}
}
}
stage('Push docker images (Parallel)') {
parallel {
stage('Push Backend') {
steps {
script {
docker.withRegistry('https://index.docker.io/v1/', DOCKERHUB_CREDENTIALS) {
docker.image(IMAGE_BACKEND).push()
}
}
}
}
stage('Push Frontend') {
steps {
script {
docker.withRegistry('https://index.docker.io/v1/', DOCKERHUB_CREDENTIALS) {
docker.image(IMAGE_FRONTEND).push()
}
}
}
}
}
}
stage('Deploy to docker swarm') {
steps {
script {
sh 'docker network inspect appnet || docker network create --driver overlay appnet'
sh "docker stack deploy -c ${COMPOSE_FILE} ${STACK_NAME}"
}
}
}
}
post {
success {
echo 'successful deployment'
}
failure {
echo 'deployment failed'
}
}
}
Note: In IMAGE_BACKEND and IMAGE_FRONTEND, write your own image names which you have written in above steps.
✨ The value of DOCKERHUB_CREDENTIALS should be the ID of the Jenkins credentials where you save your DockerHub username + access token pair.
Step-by-step (what you have to do):
1️⃣ Go to Jenkins → Manage Jenkins → Manage Credentials
2️⃣ Choose (or create) a domain (Global is fine) → Add Credentials
3️⃣ Credential Type → Username with password
Username → your DockerHub username
Password → your DockerHub access token (from DockerHub > Account Settings > Security > Access Tokens)
4️⃣ ID → Set dockerhub-creds (since your pipeline uses this name)
(Or use any ID you want — but update the pipeline env var to match)
5️⃣ Save
STEP 🔟 — Test your app
Visit http://localhost:3000 → Frontend React app
Backend API works → through Docker network
Flow of this project:
GitHub push → Jenkins Trigger
1️⃣ Clone repo
2️⃣ [Parallel] Backend & Frontend docker images build
3️⃣ [Parallel] Backend & Frontend docker images DockerHub pe push
4️⃣ appnet network check/create → Docker Swarm deploy (stack)
Result → Inside Swarm cluster,services running + Networking setup automatic
Note: We have not applied webhooks yet, to apply automation using jenkins.
Checkout next article for this.
Bye,Bye.