Cloud-native food e-commerce platform powered by self-healing Kubernetes microservices. Features auto-scaling, Istio service mesh, event-driven architecture, Jenkins + GitHub Actions CI/CD, and full observability stack.
- Architecture Overview
- Tech Stack
- Project Structure
- Installation & Setup
- CI/CD Pipeline
- Kubernetes Deployment
- Observability Stack
- Problems Faced & Solutions
- Tool-by-Tool Troubleshooting Guide
CI/CD Flow:
Code Push β GitHub β Jenkins Pipeline (16 stages) β Docker Hub β Kubernetes Cluster
ββ GitHub Actions (9 jobs) βββββββββββββββββββββββββββΆ
| Category | Technology | Version |
|---|---|---|
| Frontend | Next.js, TypeScript, Tailwind CSS | 16.2.3 |
| State management | Zustand, React Query | latest |
| Backend | Node.js, Express.js (ESM) | 20 LTS |
| Database | MongoDB | 7.0 |
| Cache | Redis | 7.2-alpine |
| Auth | JWT (access + refresh tokens) | β |
| Containerization | Docker, Docker Compose | 27.5.1 |
| Orchestration | Kubernetes | 1.28 |
| CI (local) | Jenkins | 2.516.1 |
| CI (cloud) | GitHub Actions | β |
| Registry | Docker Hub | β |
| Ingress | NGINX Ingress Controller | β |
| Autoscaling | HorizontalPodAutoscaler | β |
| Observability | Prometheus + Grafana | β |
| OS | Ubuntu 24 (WSL2) | β |
PodPlate-Platform/
βββ .github/
β βββ workflows/
β βββ ci-cd.yml # GitHub Actions 9-job pipeline
βββ frontend/ # Next.js 16 app
β βββ app/ # App router pages
β β βββ (auth)/login/
β β βββ cart/
β β βββ orders/
β β βββ products/
β β βββ restaurants/
β βββ components/
β β βββ features/
β βββ store/ # Zustand state stores
β β βββ authStore.ts
β β βββ cartStore.ts
β βββ Dockerfile
β βββ package.json
β βββ tailwind.config.js
β βββ tsconfig.json
β βββ vitest.config.ts
βββ services/
β βββ shared/ # Shared middleware & utilities
β β βββ config/
β β βββ middleware.js # CORS, helmet, rate limiting
β βββ api-gateway/ # Routes requests to services
β βββ auth-service/ # JWT auth, refresh tokens
β βββ user-service/ # User profiles
β βββ product-service/ # Product catalog + image upload
β βββ restaurant-service/ # Restaurant management
β βββ cart-service/ # Redis-backed cart
β βββ order-service/ # Order processing
β βββ payment-service/ # Payment handling
β βββ notification-service/ # Email/push notifications
βββ k8s/
β βββ namespace/ # podplate namespace
β βββ configmap/ # Non-secret environment config
β βββ secrets/ # JWT, DB, Redis secrets
β βββ storage/ # PVCs for MongoDB and Redis
β βββ mongodb/ # MongoDB StatefulSet + Service
β βββ redis/ # Redis Deployment + Service
β βββ services/ # All 9 microservice Deployments
β βββ frontend/ # Frontend Deployment + Service
β βββ ingress/ # NGINX Ingress rules
β βββ hpa/ # HorizontalPodAutoscaler
β βββ deploy.sh # One-command deploy script
βββ docker-compose.yml # Local full-stack orchestration
βββ jenkinsfile # Jenkins declarative pipeline
βββ README.md
# Install Docker
sudo apt update
sudo apt install -y docker.io docker-compose-plugin
sudo usermod -aG docker $USER
# Install Node.js 20
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# Install kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
# Install Minikube (local Kubernetes)
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
# Install Jenkins (Ubuntu/Debian)
sudo wget -O /usr/share/keyrings/jenkins-keyring.asc \
https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key
echo "deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
https://pkg.jenkins.io/debian-stable binary/" | \
sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null
sudo apt update
sudo apt install -y jenkins
sudo systemctl start jenkins
sudo systemctl enable jenkinsgit clone https://github.com/abhijitray7810/PodPlate-Platform.git
cd PodPlate-Platform
# Copy and fill in secrets
cp .env.example .env
# Edit .env with your values
# Start all 13 containers (frontend + 9 services + MongoDB + Redis)
docker compose up -d
# Verify all containers are running
docker compose ps
# Check service health
curl http://localhost:3001/health # API Gateway
curl http://localhost:3002/health # Auth Service
curl http://localhost:3000 # Frontendcd frontend
NODE_ENV=development npm ci
npm run dev # dev server on :3000
npm run build # production build
npm run lint # ESLint + Next.js lint
npm test # Vitest testscd services/auth-service
npm install
npm run dev
# Or install all at once:
for dir in services/*/; do
echo "Installing: $dir"
(cd "$dir" && npm install)
doneCheckout β Verify Tools β Install Frontend Deps β Install Shared Deps β
Install Microservice Deps β Lint β Test β Build Frontend β
Docker Login β Build Docker Images β Security Scan β
Push Docker Images β Deploy Staging β Health Check β
Deploy Production (manual approval) β Post Actions
Jenkins setup commands:
# Start Jenkins
sudo systemctl start jenkins
# Access at http://localhost:8080
# Add credentials in Jenkins UI:
# Manage Jenkins β Credentials β Global β Add Credentials
# Kind: Username with password
# ID: dockerhub
# Username: your-dockerhub-username
# Password: your-dockerhub-access-tokenKey Jenkinsfile snippet:
environment {
DOCKER_CREDS = credentials('dockerhub')
IMAGE_PREFIX = "${DOCKER_CREDS_USR}/podplate"
NODE_ENV = 'production'
}
stage('Install Frontend Dependencies') {
steps {
dir('frontend') {
// NODE_ENV=development ensures devDependencies are installed
sh 'NODE_ENV=development npm ci'
}
}
}Lint βββ¬βββΆ Test Frontend βββΆ Build Frontend βββ
ββββΆ Test Services βββββββββββββββββββββββ€
βΌ
Build Docker (matrix Γ10) βββΆ Security Scan
β
βββββββββββββββββββ΄βββββββββββββββββββ
βΌ βΌ
Deploy Staging Deploy Production
(develop branch) (main + approval)
βββββββββββββββββββ¬βββββββββββββββββββ
βΌ
Notify (Slack)
Required GitHub Secrets:
# Settings β Secrets and variables β Actions β New repository secret
DOCKER_HUB_USERNAME=abhijitray7810
DOCKER_HUB_TOKEN=your_access_token
NEXT_PUBLIC_API_URL=http://localhost:3001minikube start --memory=4096 --cpus=4
minikube addons enable ingress
minikube addons enable metrics-server# One-command full deploy
chmod +x k8s/deploy.sh
./k8s/deploy.sh up
# Or step by step:
kubectl apply -f k8s/namespace/namespace.yaml
kubectl apply -f k8s/secrets/secrets.yaml
kubectl apply -f k8s/configmap/configmap.yaml
kubectl apply -f k8s/storage/pvc.yaml
kubectl apply -f k8s/mongodb/mongodb.yaml
kubectl apply -f k8s/redis/redis.yaml
# Wait for databases
kubectl wait --for=condition=ready pod -l app=mongodb -n podplate --timeout=120s
kubectl wait --for=condition=ready pod -l app=redis -n podplate --timeout=60s
kubectl apply -f k8s/services/microservices.yaml
kubectl apply -f k8s/frontend/frontend.yaml
kubectl apply -f k8s/ingress/ingress.yaml
kubectl apply -f k8s/hpa/hpa.yaml# Check all pods
kubectl get pods -n podplate
# Check services
kubectl get svc -n podplate
# Check ingress
kubectl get ingress -n podplate
# View logs for a specific service
kubectl logs -f deployment/auth-service -n podplate
# Check HPA status
kubectl get hpa -n podplate
# Describe a crashing pod
kubectl describe pod <pod-name> -n podplate# Port-forward to test locally without ingress
kubectl port-forward svc/api-gateway-service 3001:3000 -n podplate
# Scale a deployment manually
kubectl scale deployment auth-service --replicas=3 -n podplate
# Restart a deployment (picks up new image)
kubectl rollout restart deployment/auth-service -n podplate
# Watch pod status in real time
kubectl get pods -n podplate -w
# Get all resources in namespace
kubectl get all -n podplate
# Delete and redeploy everything
./k8s/deploy.sh down
./k8s/deploy.sh up# Add Helm repos
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# Install kube-prometheus-stack (includes Prometheus + Grafana + Alertmanager)
helm install monitoring prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace
# Access Grafana dashboard
kubectl port-forward svc/monitoring-grafana 3000:80 -n monitoring
# Default login: admin / prom-operatorThis section documents every real problem encountered during the build, with exact error messages and fixes applied.
Tool: Jenkins
Stage: Docker Login
Error:
ERROR: docker-hub-username
org.jenkinsci.plugins.workflow.steps.MissingContextVariableException
Root cause: Jenkinsfile referenced credentials('docker-hub-username') but the actual Jenkins credential was stored with ID dockerhub.
Fix:
// β Before
DOCKER_HUB_USER = credentials('docker-hub-username')
DOCKER_HUB_PASS = credentials('docker-hub-password')
// β
After β use usernamePassword binding
environment {
DOCKER_CREDS = credentials('dockerhub')
// Auto-creates: DOCKER_CREDS_USR and DOCKER_CREDS_PSW
}Tool: Jenkins
Stage: Post Actions (cleanup)
Error:
MissingContextVariableException: Required context class hudson.FilePath is missing
Perhaps you forgot to surround the sh step with a step that provides this, such as: node
Root cause: The post { always } block ran sh commands outside a node context. When the pipeline failed before allocating a node, there was no workspace for cleanup commands.
Fix:
post {
always {
node('') { // wrap in node to guarantee workspace context
sh 'docker logout || true'
sh 'docker system prune -f --filter "until=24h" || true'
}
}
}Tool: npm, Next.js, Jenkins
Stage: Build Frontend
Error:
We detected multiple lockfiles and selected /workspace/package-lock.json as root
Error: Cannot find module 'tailwindcss'
Root cause: The repo had a root package.json with "workspaces": ["services/*"]. npm installed deps into root node_modules instead of frontend/node_modules. Next.js/Turbopack resolved modules from the wrong location.
Commands used to fix:
# Remove root package files from git tracking
git rm --cached package-lock.json
git rm package.json
# Add to .gitignore
echo "/package-lock.json" >> .gitignore
echo "node_modules/" >> .gitignore
git add .gitignore
git commit -m "Remove root package.json and lockfile"
# Wipe Jenkins workspace to clear stale cache
rm -rf /home/abhi/.jenkins/workspace/PodPlate-Platform-ciTool: npm, Next.js (Turbopack)
Stage: Build Frontend
Error:
Error: Cannot find module 'tailwindcss'
Error: Cannot find module 'tailwindcss-animate'
Root cause: NODE_ENV=production was set globally in the Jenkinsfile environment block. npm skips all devDependencies when NODE_ENV=production, but tailwindcss was in devDependencies.
Commands used to diagnose:
# Check how many packages were actually installed
grep -c '"node_modules/' frontend/package-lock.json
# Returned 515 in lockfile, but only 116 installed β confirmed NODE_ENV issue
wc -l frontend/package-lock.json
# 7876 lines β lockfile was correct, installation was wrongFix:
// Override NODE_ENV only for the install step
stage('Install Frontend Dependencies') {
steps {
dir('frontend') {
sh 'NODE_ENV=development npm ci'
}
}
}Tool: npm
Stage: Install Frontend Dependencies
Symptom: Jenkins log showed audited 116 packages every build despite package.json having 20+ dependencies.
Root cause: The package-lock.json committed to git was generated from an older incomplete package.json. Even though package.json was correct, npm ci used the stale lockfile.
Commands used to fix:
cd frontend
rm -rf node_modules package-lock.json
# Regenerate from current package.json
npm install
# Output: added 435 packages
git add package-lock.json
git commit -m "Fix: regenerate package-lock.json with all 435 dependencies"
git pushTool: npm, Jenkins
Stage: Install Frontend Dependencies
Symptom: Jenkins log showed:
NODE_ENV=development npm ci β added 435 packages β
npm install --save-dev ... β removed 320 packages β
audited 116 packages β back to broken state
Root cause: The install stage had three sequential commands. The third npm install --save-dev ... ran with global NODE_ENV=production and removed all devDependencies.
Fix: Reduced the entire install stage to one line:
stage('Install Frontend Dependencies') {
steps {
dir('frontend') {
sh 'NODE_ENV=development npm ci'
}
}
}Tool: Jest, Vitest, Jenkins
Stage: Test
Error:
./node_modules/.bin/jest: not found
./node_modules/.bin/vitest: not found
Root cause: Same as Problem 4 β NODE_ENV=production skipped devDependencies including test runners.
Fix: Same NODE_ENV=development npm ci fix. Also updated test commands:
sh './node_modules/.bin/vitest run --passWithNoTests || echo "No tests or skipped"'
sh './node_modules/.bin/jest --passWithNoTests || echo "No tests or skipped"'Tool: Next.js 16, Jenkins
Stage: Lint
Error:
Invalid project directory provided, no such directory: /workspace/frontend/lint
Root cause: npm run lint --if-present in Next.js 16 misinterprets lint as a directory argument instead of a script name.
Fix: Kept graceful fallback β this is a known Next.js 16 quirk:
sh 'npm run lint --if-present || echo "No lint script, skipping"'Tool: Next.js 16
Stage: Build Frontend
Warnings:
Unrecognized key(s): 'missingSuspenseWithCSRBailout' at "experimental"
Unrecognized key(s): 'swcMinify', 'eslint'
eslint configuration in next.config.js is no longer supported
Root cause: Project was originally built for Next.js 14. Running npm audit fix --force upgraded to Next.js 16, which removed these config options.
Fix: Clean up next.config.js:
// Remove deprecated keys:
// swcMinify, eslint (top-level), experimental.missingSuspenseWithCSRBailout
const nextConfig = {
// only valid Next.js 16 options here
}
module.exports = nextConfigTool: Jenkins, Docker
Stage: Push Docker Images
Symptom: Build succeeded but no images appeared in Docker Hub. Jenkins log showed:
Stage "Push Docker Images" skipped due to when conditional
Root cause: The when { branch 'main' } condition never matched because Jenkins running as a regular Pipeline (not Multibranch Pipeline) runs in detached HEAD mode β it cannot detect branch names.
Fix: Removed the when block entirely:
# Used sed to remove the when block
sed -i "/stage('Push Docker Images')/,/steps {/{/when/,/}/d}" jenkinsfile
# Then removed stray brace left by sed
sed -i "/stage('Push Docker Images')/,/steps {/{/^ }$/d}" jenkinsfile
git add jenkinsfile
git commit -m "Fix: remove branch condition from Push stage"
git pushTool: Docker Compose, Kubernetes
Stage: Deployment
Error:
SyntaxError: Unexpected string
at file:///app/shared/config/middleware.js:38
'X-Request-ID'
Root cause: Docker images were built by Jenkins from an older commit that had a syntax error in services/shared/config/middleware.js. The local fix existed but was not yet committed when Jenkins built the images.
Diagnosis commands:
# Check container logs
docker logs podplate-platform-auth-service-1 --tail 30
# In Kubernetes
kubectl logs deployment/auth-service -n podplate --tail 30
kubectl describe pod <pod-name> -n podplateFix:
# Rebuild from current source locally
docker compose down
docker compose build --no-cache
docker compose up -d
# Then commit and push so Jenkins rebuilds images
git add services/shared/config/middleware.js
git commit -m "Fix: middleware.js syntax error causing crash"
git pushTool: Git
Error:
! [rejected] main -> main (fetch first)
error: failed to push some refs
Mistake made: Used git push --force without first committing, which pushed an older state over GitHub changes.
Correct workflow:
# Always pull first when rejected
git pull --rebase origin main
git push # no force needed
# After any push, verify new commit hash:
git log --oneline -3
# Should show NEW hash, not the same one as beforeTool: Git
Symptom: Running git add package.json package-lock.json from repo root added nothing. Working tree showed clean but files not committed.
Root cause: Modified files were in frontend/package.json but git add was run from the repo root without specifying the subdirectory path.
Fix:
# Wrong β adds root package.json (no changes there)
git add package.json package-lock.json
# Correct β specify full path
git add frontend/package.json frontend/package-lock.json
# Or just add everything
git add .
git commit -m "message"
git pushTool: Jenkins
Symptom: Jenkins kept using old files (node_modules, package-lock.json) even after they were removed from git.
Root cause: Jenkins persists the workspace between builds. Files deleted from git still existed on disk in the workspace.
Fix:
# Wipe workspace from terminal
rm -rf /home/abhi/.jenkins/workspace/PodPlate-Platform-ci
# Or from Jenkins UI:
# Dashboard β PodPlate-Platform-ci β Wipe Out Workspace| Problem | Command | Fix |
|---|---|---|
| Push rejected | git pull --rebase origin main then git push |
Never force push without committing |
| Wrong files added | git add frontend/package.json |
Specify full path |
| Stale commit in Jenkins | git log --oneline -3 |
Verify new hash after push |
| Force push destroyed changes | git reflog |
Recover via reflog within 30 days |
| Problem | Command | Fix |
|---|---|---|
| devDeps skipped | NODE_ENV=development npm ci |
Override NODE_ENV for install |
| Stale lockfile | rm -rf node_modules package-lock.json && npm install |
Regenerate from scratch |
| Wrong node_modules location | Check for root package.json |
Remove root package.json |
| Package count wrong | grep -c '"node_modules/' package-lock.json |
Verify lockfile has all packages |
| Problem | Command | Fix |
|---|---|---|
| Services crash on startup | docker logs <container> --tail 30 |
Check logs for error |
| Old image used | docker compose build --no-cache |
Force rebuild |
| Port conflict | docker compose ps |
Check which ports are taken |
| Container won't stop | docker compose down --remove-orphans |
Force remove orphans |
| Problem | Command/Action | Fix |
|---|---|---|
| Credential not found | Check credential ID in Jenkins UI | ID must exactly match Jenkinsfile |
| Stage skipped (branch) | Remove when { branch } block |
Regular pipeline can't detect branch |
| Stale workspace | rm -rf /home/abhi/.jenkins/workspace/PodPlate-Platform-ci |
Wipe workspace |
| Post block fails | Wrap sh in node('') { } |
sh needs node context |
| Problem | Command | Fix |
|---|---|---|
| Pod CrashLoopBackOff | kubectl logs -f deployment/<name> -n podplate |
Check app logs |
| Pod pending | kubectl describe pod <pod> -n podplate |
Check events section |
| Service not reachable | kubectl port-forward svc/<svc> <port>:<port> -n podplate |
Test without ingress |
| Wrong image running | kubectl rollout restart deployment/<name> -n podplate |
Force new pull |
| HPA not scaling | kubectl get hpa -n podplate |
Check metrics-server is enabled |
| Problem | Command | Fix |
|---|---|---|
| tailwindcss not found | NODE_ENV=development npm ci |
devDeps must be installed |
| Multiple lockfiles warning | Remove root package-lock.json |
One lockfile per project |
| Deprecated config options | Edit next.config.js |
Remove swcMinify, eslint keys |
| Build fails on font | Install tailwindcss-animate |
Add to devDependencies |
All 10 images are public on Docker Hub:
docker pull abhijitray/podplate-frontend:latest
docker pull abhijitray/podplate-api-gateway:latest
docker pull abhijitray/podplate-auth-service:latest
docker pull abhijitray/podplate-user-service:latest
docker pull abhijitray/podplate-product-service:latest
docker pull abhijitray/podplate-restaurant-service:latest
docker pull abhijitray/podplate-cart-service:latest
docker pull abhijitray/podplate-order-service:latest
docker pull abhijitray/podplate-payment-service:latest
docker pull abhijitray/podplate-notification-service:latest# JWT
JWT_SECRET=your_jwt_secret_here
JWT_REFRESH_SECRET=your_refresh_secret_here
# MongoDB
MONGO_INITDB_ROOT_USERNAME=admin
MONGO_INITDB_ROOT_PASSWORD=your_mongo_password
# Redis
REDIS_PASSWORD=your_redis_password
# Service URLs (used by docker-compose)
FRONTEND_URL=http://localhost:3000
NEXT_PUBLIC_API_URL=http://localhost:3001
NEXT_PUBLIC_API_GATEWAY_URL=http://localhost:3001Abhijit Ray
- GitHub: @abhijitray7810
- Docker Hub: abhijitray
- LinkedIn: Abhijit Ray
If this project helped you, give it a β on GitHub!




