DEV Community

Cover image for MULTI-CONTAINER SYSTEM + REVERSE PROXY (CONSOLIDATION)
Bala Audu Musa
Bala Audu Musa

Posted on

MULTI-CONTAINER SYSTEM + REVERSE PROXY (CONSOLIDATION)

🎯 What this project consolidates

You already know CI/CD.
Now we consolidate runtime architecture.

You will practice:

  • Docker Compose (real-world usage)
  • Service-to-service communication
  • Reverse proxying with Nginx
  • Port isolation (security thinking)
  • Clean project structure
  • Debugging container networking (a core DevOps skill)

🧱 What we will build

Browser
   ↓
Nginx (port 8080)
   ↓
App container (internal only)
Enter fullscreen mode Exit fullscreen mode

πŸ“‚ Project structure (professional)

project-2-multicontainer/
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ app.js
β”‚   β”œβ”€β”€ package.json
β”‚   └── Dockerfile
β”œβ”€β”€ nginx/
β”‚   └── nginx.conf
β”œβ”€β”€ docker-compose.yml
└── README.md
Enter fullscreen mode Exit fullscreen mode

πŸ§ͺ Outcome (what you should be able to say)

β€œI run applications behind an Nginx reverse proxy using Docker Compose, isolating services and exposing only the gateway.”

🧱 CLASS 1 β€” PROJECT SETUP (START HERE)
Step 1: Create project directory

cd ~/OneDrive/Desktop
mkdir project-2-multicontainer-v2
cd project-2-multicontainer-v2
Enter fullscreen mode Exit fullscreen mode

Initialize git:

git init
Enter fullscreen mode Exit fullscreen mode

Step 2: Create folders

mkdir app nginx
Enter fullscreen mode Exit fullscreen mode

Step 3: Create docker-compose.yml

code docker-compose.yml
Enter fullscreen mode Exit fullscreen mode

Paste exactly:

services:
  app:
    build: ./app
    container_name: app
    expose:
      - "3000"

  nginx:
    image: nginx:alpine
    container_name: nginx
    ports:
      - "8080:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - app

Enter fullscreen mode Exit fullscreen mode

Save.

🧱 PROJECT 2 v2 β€” CLASS 2
Build the App Container + Configure Nginx Reverse Proxy

Goal: When you open http://localhost:8080, you see a message served by the app container, through Nginx.

1) Create the Node app (inside app/)

Go into app folder:

cd app
npm init -y
npm i express
Enter fullscreen mode Exit fullscreen mode

Create app.js:

code app.js
Enter fullscreen mode Exit fullscreen mode

Paste:

const express = require("express");
const app = express();

app.get("/", (req, res) => {
  res.send("Hello from app container (Project 2 v2) βœ…");
});

app.listen(3000, () => console.log("App running on port 3000"));

Enter fullscreen mode Exit fullscreen mode

Create Dockerfile (inside app/):

code Dockerfile
Enter fullscreen mode Exit fullscreen mode

Paste:

FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

EXPOSE 3000

CMD ["node", "app.js"]

Enter fullscreen mode Exit fullscreen mode

Go back to project root:

cd ..

Enter fullscreen mode Exit fullscreen mode

2) Create Nginx config (inside nginx/)

Create nginx/nginx.conf:

code nginx/nginx.conf
Enter fullscreen mode Exit fullscreen mode

Paste:

events {}

http {
  server {
    listen 80;

    location / {
      proxy_pass http://app:3000;
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

βœ… Key part: proxy_pass http://app:3000;

app is the service name from docker-compose.
At this stage make sure your Docker Desktop is up and running.

3) Run the stack

From project root:

docker compose up --build
Enter fullscreen mode Exit fullscreen mode

Open browser:

http://localhost:8080

Expected:
Hello from app container (Project 2 v2) βœ…

Stop:

Ctrl + C
Enter fullscreen mode Exit fullscreen mode

Then clean up:

docker compose down
Enter fullscreen mode Exit fullscreen mode

🧱 PROJECT 2 v2 β€” CLASS 3
Add Health Checks + Make Nginx β€œProduction-ish”

Goal: Add /health endpoint + improve Nginx proxy settings + verify using curl and logs.

1) Add /health endpoint (App)

Open app/app.js:

code app/app.js
Enter fullscreen mode Exit fullscreen mode

Update it to this (keep your existing / route too):

const express = require("express");
const app = express();

app.get("/", (req, res) => {
  res.send("Hello from app container (Project 2 v2) βœ…");
});

app.get("/health", (req, res) => {
  res.status(200).send("OK");
});

app.listen(3000, () => console.log("App running on port 3000"));
Enter fullscreen mode Exit fullscreen mode

Save.

2) Improve Nginx config (headers + timeouts)

Open:

code nginx/nginx.conf
Enter fullscreen mode Exit fullscreen mode

Replace with:

events {}

http {
  upstream app_upstream {
    server app:3000;
  }

  server {
    listen 80;

    location / {
      proxy_pass http://app_upstream;

      proxy_http_version 1.1;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;

      proxy_connect_timeout 5s;
      proxy_read_timeout 30s;
      proxy_send_timeout 30s;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Save.

3) Rebuild and run

From project root:

docker compose up --build
Enter fullscreen mode Exit fullscreen mode

4) Verify via curl (proof)

In a new terminal tab:

curl -i http://localhost:8080/
curl -i http://localhost:8080/health
Enter fullscreen mode Exit fullscreen mode

Expected:

/ returns 200 and your message
/health returns OK
Enter fullscreen mode Exit fullscreen mode

5) Check logs (bonus skill)

docker compose logs nginx --tail 20
docker compose logs app --tail 20
Enter fullscreen mode Exit fullscreen mode

6) Stop and clean

Ctrl + C then:

docker compose down
Enter fullscreen mode Exit fullscreen mode

🧱 PROJECT 2 v2 β€” CLASS 4
Publish to GitHub + Add CI (recruiter-ready)

Goal: Put this project on GitHub with a clean READMEand a CI workflow that validates the Node app.

1) Initialize git (if not already) + check status

From project root (project-2-multicontainer-v2):

git status
Enter fullscreen mode Exit fullscreen mode

If it says β€œnot a git repository”, run:

git init
git branch -M main
Enter fullscreen mode Exit fullscreen mode

2) Create a recruiter-friendly README (short, strong)

Create/open:

code README.md
Enter fullscreen mode Exit fullscreen mode

Paste:

# Project 2 v2 β€” Multi-Container App with Nginx Reverse Proxy (Docker Compose)

A production-style multi-container setup where a Node.js app runs privately behind an Nginx reverse proxy.

## Architecture
Browser β†’ Nginx (8080) β†’ App (internal:3000)

## What this demonstrates
- Docker Compose orchestration
- Reverse proxying with Nginx
- Service-to-service networking via Compose
- Health checks (`/health`) for operational readiness

## Run locally

`docker compose up --build`

**3) Run curl in a NEW terminal tab/window**

Open a new Git Bash window/tab and run:

Enter fullscreen mode Exit fullscreen mode

curl -i http://localhost:8080/health
curl -i http://localhost:3000


**4) Stop containers when done**

Go back to the terminal running compose and press:

Enter fullscreen mode Exit fullscreen mode

Ctrl + C


Then clean up:

Enter fullscreen mode Exit fullscreen mode

docker compose down



Enter fullscreen mode Exit fullscreen mode

Top comments (0)