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.