π― 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)
π Project structure (professional)
project-2-multicontainer/
βββ app/
β βββ app.js
β βββ package.json
β βββ Dockerfile
βββ nginx/
β βββ nginx.conf
βββ docker-compose.yml
βββ README.md
π§ͺ 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
Initialize git:
git init
Step 2: Create folders
mkdir app nginx
Step 3: Create docker-compose.yml
code docker-compose.yml
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
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
Create app.js:
code app.js
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"));
Create Dockerfile (inside app/):
code Dockerfile
Paste:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
Go back to project root:
cd ..
2) Create Nginx config (inside nginx/)
Create nginx/nginx.conf:
code nginx/nginx.conf
Paste:
events {}
http {
server {
listen 80;
location / {
proxy_pass http://app:3000;
}
}
}
β 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
Open browser:
Expected:
Hello from app container (Project 2 v2) β
Stop:
Ctrl + C
Then clean up:
docker compose down
π§± 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
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"));
Save.
2) Improve Nginx config (headers + timeouts)
Open:
code nginx/nginx.conf
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;
}
}
}
Save.
3) Rebuild and run
From project root:
docker compose up --build
4) Verify via curl (proof)
In a new terminal tab:
curl -i http://localhost:8080/
curl -i http://localhost:8080/health
Expected:
/ returns 200 and your message
/health returns OK
5) Check logs (bonus skill)
docker compose logs nginx --tail 20
docker compose logs app --tail 20
6) Stop and clean
Ctrl + C then:
docker compose down
π§± 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
If it says βnot a git repositoryβ, run:
git init
git branch -M main
2) Create a recruiter-friendly README (short, strong)
Create/open:
code README.md
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:
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:
Ctrl + C
Then clean up:
docker compose down





Top comments (0)