A production-ready, full-stack three-tier web application for tracking Body Mass Index (BMI), Basal Metabolic Rate (BMR), and daily calorie requirements with 30-day trend visualization. Features fully automated CI/CD deployment using GitHub Actions.
π Repository: https://github.com/md-sarowar-alam/single-server-3tier-webapp-github-actions.git
π Live Demo: http://44.245.64.25 (if deployed)
- Overview
- Key Features
- How It Works
- Architecture
- Tech Stack
- Project Structure
- Quick Start
- Deployment
- CI/CD with GitHub Actions
- API Documentation
- Database Schema
- Security
- Monitoring
- Troubleshooting
- Documentation
- Contributing
BMI & Health Tracker is a comprehensive health monitoring application that helps users track their health metrics over time. The application calculates BMI, BMR, and daily calorie needs based on user inputs and visualizes trends to help users monitor their health journey.
- Health Tracking: Monitor BMI, BMR, and calorie needs over time
- Historical Data: Enter measurements from past dates to build complete health history
- Trend Analysis: Visualize 30-day BMI trends to track progress
- Easy Deployment: Fully automated deployment with GitHub Actions
- Production Ready: Enterprise-grade deployment with PM2, Nginx, and PostgreSQL
β
Smart CI/CD: GitHub Actions automatically deploys to fresh or existing EC2 instances
β
Zero Configuration: Automated installation of Node.js, PostgreSQL, Nginx, PM2
β
Database Migrations: Automatic detection and execution of schema changes
β
Health Checks: Post-deployment verification with automatic rollback on failure
β
Backup System: Automatic backups before each deployment
β
Custom Dates: Enter historical measurements, not just current data
- BMI (Body Mass Index): Standard formula - weight(kg) / height(m)Β²
- BMR (Basal Metabolic Rate): Mifflin-St Jeor equation for accurate calorie burn estimation
- Daily Calorie Needs: Harris-Benedict formula with 5 activity levels
- BMI Categories: Color-coded (Underweight, Normal, Overweight, Obese)
- PostgreSQL Database: Reliable storage with connection pooling
- Custom Measurement Dates: Enter historical data, not just current
- 30-Day Trend Visualization: Interactive Chart.js graphs
- Historical View: Complete measurement history with dates
- Statistics Dashboard: Average BMI, latest measurements, total records
- GitHub Actions CI/CD: Fully automated deployment pipeline
- Smart Detection: Auto-detects first-time vs update deployment
- Zero Configuration: Installs Node.js, PostgreSQL, Nginx, PM2 automatically
- Database Migrations: Automatic schema updates with tracking
- Health Checks: Post-deployment verification
- Rollback Support: Quick recovery to previous version
- Backup System: Automatic backups before updates (keeps last 5)
- PM2 Process Management: Auto-restart, logs, monitoring
- Nginx Reverse Proxy: Static file serving + API proxying
- CORS Configuration: Secure cross-origin requests
- Environment-Based Config: Separate dev/prod settings
- Error Handling: Graceful error messages, logging
- Responsive Design: Card-based UI, mobile-friendly
βββββββββββββββ
β User Enters β
β Measurement β
β Details β
ββββββββ¬βββββββ
β
β
ββββββββββββββββββββββββββββββββββββββββ
β Frontend Validation & Form Submit β
β - Height (cm), Weight (kg), Age β
β - Gender, Activity Level β
β - Measurement Date (can be past) β
ββββββββ¬ββββββββββββββββββββββββββββββββ
β
β POST /api/measurements
ββββββββββββββββββββββββββββββββββββββββ
β Backend Processing β
β 1. Validate inputs β
β 2. Calculate BMI = weight/heightΒ² β
β 3. Calculate BMR (Mifflin-St Jeor) β
β 4. Calculate Daily Calories β
β 5. Determine BMI Category β
ββββββββ¬ββββββββββββββββββββββββββββββββ
β
β INSERT INTO measurements
ββββββββββββββββββββββββββββββββββββββββ
β PostgreSQL Database β
β - Store all measurements β
β - Index by date for fast queries β
ββββββββ¬ββββββββββββββββββββββββββββββββ
β
β Return created measurement
ββββββββββββββββββββββββββββββββββββββββ
β Frontend Updates β
β 1. Display success message β
β 2. Refresh statistics cards β
β 3. Update 30-day trend chart β
β 4. Add to measurement history β
ββββββββββββββββββββββββββββββββββββββββ
BMI = weight(kg) / (height(m))Β²Categories:
- Underweight: BMI < 18.5
- Normal: 18.5 β€ BMI < 25
- Overweight: 25 β€ BMI < 30
- Obese: BMI β₯ 30
// For Men
BMR = (10 Γ weight_kg) + (6.25 Γ height_cm) - (5 Γ age) + 5
// For Women
BMR = (10 Γ weight_kg) + (6.25 Γ height_cm) - (5 Γ age) - 161Daily Calories = BMR Γ Activity MultiplierActivity Multipliers:
- Sedentary (little/no exercise): 1.2
- Lightly active (1-3 days/week): 1.375
- Moderately active (3-5 days/week): 1.55
- Very active (6-7 days/week): 1.725
- Extra active (2x/day): 1.9
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PRESENTATION TIER β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β React 18.2 + Vite 5.0 (Single Page App) β β
β β - MeasurementForm.jsx (User Input) β β
β β - TrendChart.jsx (Chart.js Visualization) β β
β β - App.jsx (Main Component) β β
β β - api.js (Axios HTTP Client) β β
β β Port: 5173 (dev) | 80/443 via Nginx (prod) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β HTTP REST API
β /api/measurements, /api/measurements/stats, etc.
ββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββ
β APPLICATION TIER β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Node.js + Express 4.18 REST API (PM2 managed) β β
β β - routes.js (API Endpoints) β β
β β - calculations.js (BMI/BMR Logic) β β
β β - server.js (Express App) β β
β β - db.js (PostgreSQL Connection Pool) β β
β β Port: 3000 (PM2 process: bmi-backend) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β PostgreSQL Protocol
β Connection Pool (max 20 connections)
ββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββ
β DATA TIER β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β PostgreSQL 12+ Database β β
β β - Database: bmidb β β
β β - User: bmi_user β β
β β - Table: measurements (11 columns) β β
β β - Indexes: date, created_at, BMI β β
β β Port: 5432 (localhost only, not exposed) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Internet
β
ββββββββββββββββββββββββββββββββββββββββ
β AWS Security Group (Firewall) β
β - Port 22 (SSH) - Your IP only β
β - Port 80 (HTTP) - 0.0.0.0/0 β
β - Port 443 (HTTPS) - 0.0.0.0/0 β
ββββββββββββββββββββ¬ββββββββββββββββββββ
β
ββββββββββββββββββββββββββββββββββββββββββββββββββ
β EC2 Instance (Ubuntu 22.04 LTS) β
β β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β β Nginx Web Server (Port 80/443) β β
β β - Serves /var/www/bmi-health-tracker/ β β
β β - Reverse proxy /api/* β localhost:3000 β β
β β - SSL/TLS with Certbot (optional) β β
β βββββββββββββββ¬βββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββΌβββββββββββββββββββββββββββββ β
β β PM2 Process Manager β β
β β - Process name: bmi-backend β β
β β - Auto-restart on crash/code changes β β
β β - Startup on system reboot β β
β β - Log management (error.log, out.log) β β
β βββββββββββββββ¬βββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββΌβββββββββββββββββββββββββββββ β
β β Express API Server (Port 3000) β β
β β - REST API endpoints β β
β β - BMI/BMR calculations β β
β β - CORS middleware β β
β β - PostgreSQL connection pool β β
β βββββββββββββββ¬βββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββΌβββββββββββββββββββββββββββββ β
β β PostgreSQL Database (Port 5432) β β
β β - Database: bmidb β β
β β - User: bmi_user (password protected) β β
β β - Listens only on localhost β β
β β - Connection pooling enabled β β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β β
β GitHub Actions (CI/CD Pipeline) β
β - SSH connection for automated deployment β
β - Smart detection (first-time vs update) β
β - Automatic backups before updates β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
### Technology Stack by Tier
#### Presentation Tier (Frontend)
- **Framework:** React 18 with functional components and hooks
- **Build Tool:** Vite 5 for fast development and optimized production builds
- **UI/UX:** Modern card-based design with gradients and animations
- **Charts:** Chart.js for trend visualization
- **HTTP Client:** Axios for API communication
- **Routing:** SPA with client-side routing
#### Application Tier (Backend)
- **Runtime:** Node.js 18+ with ES modules
- **Framework:** Express.js for REST API
- **Process Manager:** PM2 for production process management
- **Database Client:** node-postgres (pg) for PostgreSQL
- **Validation:** Custom middleware for input validation
- **Error Handling:** Centralized error handling middleware
#### Data Tier (Database)
- **Database:** PostgreSQL 12+ for relational data storage
- **Connection:** Connection pooling for optimal performance
- **Migrations:** SQL migration files for schema management
- **Backup:** Automated backup scripts in database folder
## π Features
### User Features
- β
**Modern Professional UI** - Card-based design with gradient backgrounds and smooth animations
- β
**Real-time Stats Dashboard** - Visual stat cards showing BMI, BMR, daily calories, and total records
- β
**BMI Calculation** - Instant BMI calculation with health categorization
- β
**BMR (Basal Metabolic Rate)** - Calculate your resting metabolic rate using scientifically-proven formulas
- β
**Daily Calorie Needs** - Personalized recommendations based on activity level
- β
**30-Day BMI Trend** - Beautiful chart visualization of your progress over time
- β
**Historical Tracking** - View all measurements with color-coded badges and sorting
- β
**Fully Responsive** - Optimized for desktop, tablet, and mobile devices
- β
**Enhanced UX** - Loading states, success/error alerts with animations, empty state messages
- β
**Professional Form Design** - Multi-column responsive forms with focus states and validation
### Technical Features
- β
**RESTful API** - Standard HTTP methods and status codes
- β
**Database Migrations** - Version-controlled schema changes
- β
**Connection Pooling** - Efficient database connection management
- β
**Error Handling** - Comprehensive error handling with proper HTTP status codes
- β
**CORS Configuration** - Environment-based CORS setup
- β
**Input Validation** - Backend validation for all user inputs
- β
**SQL Injection Protection** - Parameterized queries throughout
- β
**Health Check Endpoint** - `/health` endpoint for monitoring
- β
**Process Management** - PM2 for zero-downtime deployments
- β
**Reverse Proxy** - Nginx for static file serving and API proxying
- β
**SSL/TLS Support** - HTTPS with Let's Encrypt (Certbot)
## π§ Tech Stack
### Frontend
| Technology | Version | Purpose |
|------------|---------|---------|
| React | 18.3.1 | UI framework with hooks and functional components |
| Vite | 5.4.2 | Build tool for fast development and optimized production builds |
| Chart.js | 4.4.1 | Data visualization for BMI trends |
| react-chartjs-2 | 5.2.0 | React wrapper for Chart.js |
| Axios | 1.6.5 | HTTP client for API requests |
### Backend
| Technology | Version | Purpose |
|------------|---------|---------|
| Node.js | 18+ | JavaScript runtime |
| Express | 4.18.2 | Web framework for REST API |
| pg (node-postgres) | 8.11.3 | PostgreSQL client |
| cors | 2.8.5 | Cross-origin resource sharing |
| dotenv | 16.3.1 | Environment variable management |
### Database
| Technology | Version | Purpose |
|------------|---------|---------|
| PostgreSQL | 12+ | Relational database for data persistence |
### DevOps & Infrastructure
| Technology | Purpose |
|------------|---------|
| PM2 | Process manager for Node.js in production |
| Nginx | Reverse proxy and static file server |
| UFW | Firewall configuration |
| Certbot | SSL certificate management (Let's Encrypt) |
| AWS EC2 | Cloud hosting (Ubuntu 22.04 LTS) |
## ποΈ Project Structure
single-ec2-three-tier-app/ β βββ frontend/ # React Frontend (Presentation Tier) β βββ src/ β β βββ components/ β β β βββ MeasurementForm.jsx # Input form for health data β β β βββ TrendChart.jsx # Chart.js BMI trend visualization β β βββ App.jsx # Main React component β β βββ api.js # Axios API client configuration β β βββ index.css # Global styles β β βββ main.jsx # React entry point β βββ index.html # HTML template β βββ vite.config.js # Vite build configuration β βββ package.json # Frontend dependencies β βββ dist/ # Production build output (generated) β βββ backend/ # Node.js Backend (Application Tier) β βββ src/ β β βββ server.js # Express server setup and middleware β β βββ routes.js # API route definitions β β βββ db.js # PostgreSQL connection pool β β βββ calculations.js # BMI, BMR, calorie calculations β βββ migrations/ β β βββ 001_create_measurements.sql # Database schema migration β βββ ecosystem.config.js # PM2 configuration β βββ package.json # Backend dependencies β βββ .env # Environment variables (not in git) β βββ database/ # Database Management Scripts β βββ setup-database.sh # All-in-one database setup script β βββ backup-database.sh # Database backup script (generated) β βββ restore-database.sh # Database restore script (generated) β βββ GitHubActions-CICD/ # CI/CD Course Materials β βββ Session-1-GitHub-Actions/ β βββ Session-2-Deploy-BMI-App/ β βββ Monitoring/ β βββ Session-1-Prometheus/ β βββ Session-2-Grafana/ β βββ deploy.sh # Comprehensive deployment automation script β βββ README.md # This file βββ AGENT.md # Complete project documentation βββ CONNECTIVITY.md # Three-tier connectivity configuration βββ BMI_Health_Tracker_Deployment_Readme.md # Manual deployment guide βββ DEPLOYMENT_CHECKLIST.md # Pre-deployment checklist βββ DEPLOYMENT_READY.md # Deployment readiness verification βββ FINAL_AUDIT.md # Security and quality audit βββ DevOpsReadme.md # DevOps practices and procedures
### File Purposes
#### Backend Files
- **server.js:** Express server initialization, middleware setup, CORS configuration
- **routes.js:** All API endpoints (GET /api/measurements, POST /api/measurements, GET /health)
- **db.js:** PostgreSQL connection pool configuration
- **calculations.js:** Health metric calculation functions (BMI, BMR, daily calories)
- **.env:** Database credentials and configuration (DATABASE_URL, PORT)
#### Frontend Files
- **App.jsx:** Main component with state management, API calls, and child component coordination
- **MeasurementForm.jsx:** Form component for user input with validation
- **TrendChart.jsx:** Chart.js wrapper for BMI trend visualization
- **api.js:** Axios instance with base URL configuration
- **index.css:** Global styles, gradient backgrounds, animations
#### Infrastructure Files
- **deploy.sh:** Automated deployment script (backs up, builds, deploys, configures)
- **database/setup-database.sh:** Database initialization with migrations and sample data
- **ecosystem.config.js:** PM2 configuration for process management
## π Quick Start
### Prerequisites
Before you begin, ensure you have the following installed:
| Software | Version | Installation |
|----------|---------|--------------|
| Node.js | 18+ (LTS) | [nodejs.org](https://nodejs.org) or use NVM |
| PostgreSQL | 12+ | [postgresql.org](https://postgresql.org) |
| npm | 9+ | Included with Node.js |
| Git | Latest | [git-scm.com](https://git-scm.com) |
### Local Development Setup
#### 1. Clone the Repository
```bash
git clone <your-repo-url>
cd single-ec2-three-tier-app
Option A: Automated Setup (Recommended)
cd database
chmod +x setup-database.sh
./setup-database.shThis script will:
- Install PostgreSQL (if needed)
- Create database and user
- Run migrations
- Create backup/restore scripts
- Optionally add sample data
Option B: Manual Setup
# Create PostgreSQL user
sudo -u postgres createuser --pwprompt bmi_user
# Enter password when prompted: your_password
# Create database
sudo -u postgres createdb -O bmi_user bmidb
# Run migrations
cd backend
psql -U bmi_user -d bmidb -h localhost -f migrations/001_create_measurements.sql
psql -U bmi_user -d bmidb -h localhost -f migrations/002_add_measurement_date.sqlcd backend
# Install dependencies
npm install
# Create .env file
cat > .env << EOF
PORT=3000
DATABASE_URL=postgresql://bmi_user:your_password@localhost:5432/bmidb
NODE_ENV=development
EOF
# Verify database connection
npm run dev
# Should see: "Connected to PostgreSQL" and "Server running on port 3000"cd ../frontend
# Install dependencies
npm install
# Start development server
npm run dev
# Should see: "Local: http://localhost:5173"Open your browser and navigate to:
- Frontend: http://localhost:5173
- Backend API: http://localhost:3000/api/measurements
- Health Check: http://localhost:3000/health
-
Test Backend:
curl http://localhost:3000/health # Should return: {"status":"ok","database":"connected"} -
Test Database:
curl http://localhost:3000/api/measurements # Should return: [] (empty array if no data) -
Test Frontend:
- Enter sample data in the form
- Click "Calculate & Save"
- Verify stats update and chart displays
- Development:
http://localhost:3000/api - Production:
http://your-domain/api
GET /api/measurementsDescription: Retrieves all measurements ordered by date (newest first)
Response:
[
{
"id": 1,
"height": 175,
"weight": 70,
"age": 30,
"gender": "male",
"activity_level": "moderately",
"bmi": 22.86,
"bmi_category": "Normal",
"bmr": 1680,
"daily_calories": 2604,
"measurement_date": "2025-12-15",
"created_at": "2025-12-15T10:30:00.000Z"
}
]Status Codes:
200 OK- Success500 Internal Server Error- Database error
POST /api/measurements
Content-Type: application/jsonDescription: Creates a new measurement with calculated health metrics
Request Body:
{
"height": 175,
"weight": 70,
"age": 30,
"gender": "male",
"activity_level": "moderately",
"measurementDate": "2025-12-15" // Optional: defaults to current date
}Field Validations:
height(required): Number, 50-300 cmweight(required): Number, 20-500 kgage(required): Integer, 10-120 yearsgender(required): String, "male" or "female"activity_level(required): String, one of:- "sedentary"
- "lightly"
- "moderately"
- "very"
- "extra"
Response:
{
"id": 1,
"height": 175,
"weight": 70,
"age": 30,
"gender": "male",
"activity_level": "moderately",
"bmi": 22.86,
"bmi_category": "Normal",
"bmr": 1680,
"daily_calories": 2604,
"measurement_date": "2025-12-15",
"created_at": "2025-12-15T10:30:00.000Z"
}Status Codes:
201 Created- Measurement created successfully400 Bad Request- Invalid input data500 Internal Server Error- Database error
GET /healthDescription: Checks API and database connectivity
Response:
{
"status": "ok",
"database": "connected"
}Status Codes:
200 OK- All systems operational500 Internal Server Error- Database connection failed
# Get all measurements
curl http://localhost:3000/api/measurements
# Create new measurement
curl -X POST http://localhost:3000/api/measurements \
-H "Content-Type: application/json" \
-d '{
"height": 175,
"weight": 70,
"age": 30,
"gender": "male",
"activity_level": "moderately"
}'
# Health check
curl http://localhost:3000/healthimport axios from 'axios';
const api = axios.create({
baseURL: 'http://localhost:3000/api'
});
// Get all measurements
const measurements = await api.get('/measurements');
// Create measurement
const newMeasurement = await api.post('/measurements', {
height: 175,
weight: 70,
age: 30,
gender: 'male',
activity_level: 'moderately'
});CREATE TABLE measurements (
id SERIAL PRIMARY KEY,
height DECIMAL(5,2) NOT NULL, -- Height in cm (e.g., 175.50)
weight DECIMAL(5,2) NOT NULL, -- Weight in kg (e.g., 70.25)
age INTEGER NOT NULL, -- Age in years
gender VARCHAR(10) NOT NULL, -- 'male' or 'female'
activity_level VARCHAR(20) NOT NULL, -- Activity level
bmi DECIMAL(4,2) NOT NULL, -- Calculated BMI
bmi_category VARCHAR(20) NOT NULL, -- BMI category
bmr INTEGER NOT NULL, -- Calculated BMR
daily_calories INTEGER NOT NULL, -- Calculated daily calories
measurement_date DATE NOT NULL DEFAULT CURRENT_DATE, -- Date of measurement
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- Record creation time
);
-- Index for faster date-based queries
CREATE INDEX idx_measurements_created_at ON measurements(created_at DESC);
CREATE INDEX idx_measurements_measurement_date ON measurements(measurement_date DESC);| Field | Type | Constraints | Description |
|---|---|---|---|
| id | SERIAL | PRIMARY KEY | Auto-incrementing unique identifier |
| height | DECIMAL(5,2) | NOT NULL | Height in centimeters (e.g., 175.50) |
| weight | DECIMAL(5,2) | NOT NULL | Weight in kilograms (e.g., 70.25) |
| age | INTEGER | NOT NULL | Age in years |
| gender | VARCHAR(10) | NOT NULL | Gender: 'male' or 'female' |
| activity_level | VARCHAR(20) | NOT NULL | One of: sedentary, lightly, moderately, very, extra |
| bmi | DECIMAL(4,2) | NOT NULL | Calculated Body Mass Index |
| bmi_category | VARCHAR(20) | NOT NULL | One of: Underweight, Normal, Overweight, Obese |
| bmr | INTEGER | NOT NULL | Calculated Basal Metabolic Rate (calories/day) |
| daily_calories | INTEGER | NOT NULL | Calculated daily calorie needs |
| measurement_date | DATE | NOT NULL, DEFAULT CURRENT_DATE | Date when measurement was taken |
| created_at | TIMESTAMP | DEFAULT NOW() | Record creation timestamp |
The application uses the following queries:
Get All Measurements:
SELECT * FROM measurements
ORDER BY measurement_date DESC, created_at DESC;Insert Measurement:
INSERT INTO measurements (
height, weight, age, gender, activity_level,
bmi, bmi_category, bmr, daily_calories, measurement_date
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING *;Get Recent Measurements (for trend chart):
SELECT * FROM measurements
WHERE measurement_date >= CURRENT_DATE - INTERVAL '30 days'
ORDER BY measurement_date ASC;The project includes a comprehensive deployment script that automates the entire deployment process.
# Basic deployment
./deploy.sh
# Fresh deployment (clean install)
./deploy.sh --fresh
# Skip Nginx reconfiguration
./deploy.sh --skip-nginx
# Skip backup creation
./deploy.sh --skip-backupWhat the deploy script does:
- β Checks all prerequisites (Node.js, PostgreSQL, Nginx, PM2)
- β Creates backup of current deployment
- β Installs backend dependencies
- β Runs database migrations
- β Builds frontend for production
- β Deploys frontend to
/var/www/bmi-health-tracker - β Configures PM2 for backend process management
- β Sets up Nginx reverse proxy
- β Runs health checks
- β Displays deployment summary
For detailed manual deployment instructions, see:
- BMI_Health_Tracker_Deployment_Readme.md - Complete step-by-step guide
- DEPLOYMENT_CHECKLIST.md - Pre-deployment checklist
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β AWS EC2 Instance β
β Ubuntu 22.04 LTS (t2.micro) β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββ β
β β Nginx (Port 80/443) β β
β β - Serves frontend from /var/www β β
β β - Reverse proxy /api/* β localhost:3000 β β
β ββββββββββββββββββ¬ββββββββββββββββββββββββββββ β
β β β
β ββββββββββββββββββΌββββββββββββββββββββββββββββ β
β β PM2 Process Manager β β
β β - Manages Node.js backend β β
β β - Auto-restart on failure β β
β β - Auto-start on server reboot β β
β ββββββββββββββββββ¬ββββββββββββββββββββββββββββ β
β β β
β ββββββββββββββββββΌββββββββββββββββββββββββββββ β
β β Node.js Backend (Port 3000) β β
β β - Express API β β
β β - Health calculations β β
β ββββββββββββββββββ¬ββββββββββββββββββββββββββββ β
β β β
β ββββββββββββββββββΌββββββββββββββββββββββββββββ β
β β PostgreSQL (Port 5432) β β
β β - Database: bmidb β β
β β - User: bmi_user β β
β ββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Security Group Rules: β
β - Port 22 (SSH) - Your IP only β
β - Port 80 (HTTP) - 0.0.0.0/0 β
β - Port 443 (HTTPS) - 0.0.0.0/0 β
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
After deployment:
-
Test the Application:
curl http://your-ec2-ip/api/measurements
-
Setup SSL (Optional but Recommended):
sudo certbot --nginx -d your-domain.com
-
Monitor Logs:
# Backend logs pm2 logs bmi-backend # Nginx logs sudo tail -f /var/log/nginx/bmi-*.log # Database logs sudo tail -f /var/log/postgresql/postgresql-12-main.log
-
Setup Monitoring:
- See GitHubActions-CICD/Monitoring/ for Prometheus and Grafana setup
This project supports fully automated deployment using GitHub Actions. The workflow handles both initial deployment and updates automatically.
GitHub Actions will:
- Install Node.js, PostgreSQL, Nginx, PM2
- Setup database and run migrations
- Clone repository and deploy application
- Configure Nginx reverse proxy
- Start services with PM2
Requirements:
- Fresh Ubuntu EC2 instance with SSH access
- 4 GitHub Secrets:
EC2_HOST,EC2_USER,EC2_SSH_KEY,DB_PASSWORD
GitHub Actions will:
- Create backup before deployment
- Pull latest code
- Run new migrations
- Rebuild and restart services
- Verify with health checks
- β Smart Detection: Automatically detects first-time vs update deployment
- β Automated Setup: Installs all prerequisites on fresh servers
- β Manual Deployment: Trigger deployments from GitHub UI with options
- β Backup Creation: Automatic backup before each update
- β Database Migrations: Automatically runs new SQL migrations
- β Health Checks: Verifies deployment success
- β Rollback Support: Quick rollback to previous versions
- β Deployment Summary: Detailed status reports in GitHub Actions
-
Launch EC2 instance (Ubuntu 22.04, ports 22/80/443 open)
-
Run minimal setup on EC2:
sudo apt update && sudo apt install -y git git clone https://github.com/md-sarowar-alam/single-server-3tier-webapp-github-actions.git temp && cd temp chmod +x scripts/initial-ec2-setup.sh && ./scripts/initial-ec2-setup.sh
-
Generate SSH key locally:
ssh-keygen -t ed25519 -C "github-actions" -f ~/.ssh/github-actions-key -N "" cat ~/.ssh/github-actions-key.pub # Copy this
-
Add public key to EC2:
echo "PASTE_PUBLIC_KEY_HERE" >> ~/.ssh/authorized_keys
-
Add 4 GitHub Secrets:
EC2_HOST: Your EC2 IP (e.g.,44.245.64.25)EC2_USER:ubuntuEC2_SSH_KEY: (entire private key fromcat ~/.ssh/github-actions-key)DB_PASSWORD: Choose a strong password for PostgreSQL
-
Push to trigger deployment:
git add . && git commit -m "Initial deployment" && git push origin main
-
Generate SSH key and add to EC2 (steps 3-4 above)
-
Add 3 GitHub Secrets (EC2_HOST, EC2_USER, EC2_SSH_KEY) - DB_PASSWORD optional
-
Push to main:
```bash git add . git commit -m "Enable CI/CD" git push origin main
Watch deployment progress in GitHub Actions tab. First deployment takes 5-10 minutes.
graph TD
A[Push to main] --> B{First Time?}
B -->|Yes| C[Install Prerequisites]
B -->|No| D[Create Backup]
C --> E[Setup Database]
D --> F[Pull Latest Code]
E --> G[Clone Repository]
F --> H{New Migrations?}
G --> I[Run Migrations]
H -->|Yes| I
H -->|No| J[Deploy Backend]
I --> J
J --> K[Build Frontend]
K --> L[Deploy to Nginx]
L --> M[Health Checks]
M -->|Pass| N[Success β
]
M -->|Fail| O[Rollback Available]
Trigger deployments manually with options:
- Backend Only: Deploy only backend changes
- Frontend Only: Deploy only frontend changes
- Skip Health Check: Skip post-deployment verification
Go to: GitHub Actions β Deploy to AWS EC2 β Run workflow
If deployment fails or causes issues, use the rollback script:
ssh [email protected]
cd ~/single-server-3tier-webapp
./scripts/rollback.shThe script will:
- Show available backups
- Let you select which backup to restore
- Restore files and restart services
- Run health checks
- DATABASE_SETUP.md - Database password configuration guide
- .github/workflows/deploy.yml - Workflow configuration
- scripts/rollback.sh - Rollback script for quick recovery
- scripts/initial-ec2-setup.sh - Minimal EC2 preparation
- GITHUB_ACTIONS_SETUP.md - Complete setup guide
- .github/workflows/deploy.yml - Workflow configuration
- scripts/rollback.sh - Rollback script
- β
Environment Variables: Sensitive credentials stored in
.env(not in git) - β CORS Configuration: Environment-based CORS for development/production
- β SQL Injection Protection: Parameterized queries with pg library
- β Input Validation: Backend validation for all user inputs
- β Error Handling: No internal details exposed in error responses
- β HTTP Security Headers: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection
- β Server Tokens: Nginx version hidden
- β Firewall (UFW): Only necessary ports open (22, 80, 443)
- β SSH Access: Key-based authentication recommended
- β Database Access: PostgreSQL bound to localhost only
- β SSL/TLS: HTTPS with Let's Encrypt certificates
- β Process Isolation: PM2 runs as non-root user
-
Change Default Credentials:
# Update database password in .env nano backend/.env # Update PostgreSQL password sudo -u postgres psql -c "ALTER USER bmi_user WITH PASSWORD 'new_secure_password';"
-
Enable Automatic Security Updates:
sudo apt install unattended-upgrades sudo dpkg-reconfigure --priority=low unattended-upgrades
-
Setup Fail2Ban:
sudo apt install fail2ban sudo systemctl enable fail2ban -
Regular Backups:
# Use provided backup script ./database/backup-database.sh -
Monitor Logs Regularly:
# Check for suspicious activity sudo tail -f /var/log/nginx/bmi-access.log pm2 logs bmi-backend
# View process status
pm2 status
# View real-time logs
pm2 logs bmi-backend
# Monitor CPU/Memory
pm2 monit
# View process details
pm2 describe bmi-backend# Check application health
curl http://localhost/health
# Expected response:
# {"status":"ok","database":"connected"}For comprehensive monitoring with Prometheus and Grafana, see:
- GitHubActions-CICD/Monitoring/Session-1-Prometheus/ - Prometheus setup
- GitHubActions-CICD/Monitoring/Session-2-Grafana/ - Grafana dashboards
| Metric | Tool | Alert Threshold |
|---|---|---|
| CPU Usage | PM2 / Prometheus | > 80% |
| Memory Usage | PM2 / Prometheus | > 85% |
| Response Time | Nginx logs / Prometheus | > 1000ms |
| Error Rate | PM2 logs / Application | > 5% |
| Database Connections | PostgreSQL | > 80% of max |
| Disk Space | System | > 90% |
Symptoms:
Error: Connection refused
Database connection failed
Solutions:
# Check if PostgreSQL is running
sudo systemctl status postgresql
# Start PostgreSQL if stopped
sudo systemctl start postgresql
# Verify database exists
sudo -u postgres psql -l | grep bmidb
# Check credentials in .env
cat backend/.env
# Test connection manually
psql -U bmi_user -d bmidb -h localhostSymptoms:
pm2 status shows "errored"
Port 3000 already in use
Solutions:
# Check PM2 logs
pm2 logs bmi-backend --lines 50
# Kill process on port 3000
sudo lsof -ti:3000 | xargs kill -9
# Restart PM2 process
pm2 restart bmi-backend
# Check for missing dependencies
cd backend && npm installSymptoms:
npm run build fails
dist folder not created
Solutions:
# Clean and rebuild
cd frontend
rm -rf node_modules package-lock.json dist
npm install
npm run build
# Check for syntax errors
npm run lint
# Verify Node.js version
node -v # Should be 18+Symptoms:
502 Bad Gateway error
Cannot reach /api/* endpoints
Solutions:
# Check backend is running
pm2 status
# Test backend directly
curl http://localhost:3000/health
# Check Nginx configuration
sudo nginx -t
# Verify proxy_pass in Nginx config
sudo cat /etc/nginx/sites-available/bmi-health-tracker | grep proxy_pass
# Restart Nginx
sudo systemctl restart nginxSymptoms:
Access to XMLHttpRequest blocked by CORS policy
Solutions:
// In backend/.env, ensure CORS is configured
NODE_ENV=production // or development
// In backend/src/server.js, verify CORS setup
const corsOptions = {
origin: process.env.NODE_ENV === 'production'
? 'http://your-domain.com'
: 'http://localhost:5173'
};Symptoms:
relation "measurements" does not exist
Solutions:
# Run migrations manually
cd backend
psql -U bmi_user -d bmidb -h localhost -f migrations/001_create_measurements.sql
psql -U bmi_user -d bmidb -h localhost -f migrations/002_add_measurement_date.sql
# Verify table exists
psql -U bmi_user -d bmidb -h localhost -c "\dt"| Component | Log Location | Command |
|---|---|---|
| Backend (PM2) | PM2 logs | pm2 logs bmi-backend |
| Nginx Access | /var/log/nginx/bmi-access.log | sudo tail -f /var/log/nginx/bmi-access.log |
| Nginx Error | /var/log/nginx/bmi-error.log | sudo tail -f /var/log/nginx/bmi-error.log |
| PostgreSQL | /var/log/postgresql/ | sudo tail -f /var/log/postgresql/postgresql-*-main.log |
| System | /var/log/syslog | sudo tail -f /var/log/syslog |
Comprehensive documentation is available in the following files:
| Document | Description |
|---|---|
| README.md | This file - Project overview, architecture, and quick start |
| AGENT.md | Complete reconstruction guide - Everything needed to recreate the project |
| GITHUB_ACTIONS_SETUP.md | CI/CD setup guide for fresh and existing deployments |
| DATABASE_SETUP.md | Database configuration and password management |
| CONNECTIVITY.md | Three-tier architecture and connectivity details |
| IMPLEMENTATION_GUIDE.md | Step-by-step manual AWS EC2 deployment guide |
| AppUpdate.md | Manual update procedures and app updates |
| ADMINISTRATOR_DB.md | Database administration guide |
Backend API:
# Health check
curl http://localhost:3000/health
# Get measurements
curl http://localhost:3000/api/measurements
# Create measurement
curl -X POST http://localhost:3000/api/measurements \
-H "Content-Type: application/json" \
-d '{"height":175,"weight":70,"age":30,"sex":"male","activity_level":"moderately","measurement_date":"2025-12-18"}'Database:
# Connect to database
psql -U bmi_user -d bmidb -h localhost
# Run test queries
SELECT COUNT(*) FROM measurements;
SELECT * FROM measurements ORDER BY created_at DESC LIMIT 5;Use Apache Bench or similar tools:
# Install Apache Bench
sudo apt install apache2-utils
# Test API endpoint (100 requests, 10 concurrent)
ab -n 100 -c 10 http://localhost:3000/api/measurementsContributions are welcome! Here's how you can help:
- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
- Follow existing code style and conventions
- Add tests for new features
- Update documentation for significant changes
- Test deployment locally before submitting PR
- Include clear commit messages
For issues, questions, or contributions:
- Review the Troubleshooting section
- Check AGENT.md for complete technical details
- Review GitHub Actions workflow logs for deployment issues
- Check log files for error details:
- Backend:
pm2 logs bmi-backend - Nginx:
sudo tail -f /var/log/nginx/bmi-error.log - Database:
sudo tail -f /var/log/postgresql/postgresql-*-main.log
- Backend:
- Create an issue in the GitHub repository with:
- Clear description of the problem
- Steps to reproduce
- Expected vs actual behavior
- Environment details (OS, Node version, etc.)
This BMI & Health Tracker demonstrates:
β
Full-Stack Development: React frontend + Express backend + PostgreSQL database
β
Modern DevOps: GitHub Actions CI/CD, automated deployment, rollback support
β
Production Ready: PM2 process management, Nginx reverse proxy, health checks
β
Database Design: Proper schema, indexes, migrations, connection pooling
β
Security: Environment variables, CORS, input validation, localhost database
β
Best Practices: Error handling, logging, backups, documentation
β
Cloud Deployment: AWS EC2, security groups, scalable architecture
β
Monitoring: Health endpoints, PM2 monitoring, log management
# 1. Launch EC2 (Ubuntu 22.04, ports 22/80/443)
# 2. SSH and run minimal setup
ssh ubuntu@YOUR_EC2_IP
sudo apt update && sudo apt install -y git
git clone https://github.com/md-sarowar-alam/single-server-3tier-webapp-github-actions.git temp && cd temp
chmod +x scripts/initial-ec2-setup.sh && ./scripts/initial-ec2-setup.sh
# 3. Generate SSH key locally, add to EC2
# 4. Configure 4 GitHub Secrets (EC2_HOST, EC2_USER, EC2_SSH_KEY, DB_PASSWORD)
# 5. Push to main β automatic deployment!
git push origin main# 1. Generate SSH key, add to EC2
# 2. Configure 3 GitHub Secrets (DB_PASSWORD optional)
# 3. Push to main β automatic update with backup!
git push origin main# Backend
cd backend && npm install && npm run dev
# Frontend (separate terminal)
cd frontend && npm install && npm run devRepository: https://github.com/md-sarowar-alam/single-server-3tier-webapp-github-actions.git
Last Updated: December 18, 2025
Version: 2.0.0 (with GitHub Actions CI/CD)
Status: Production Ready β
π§βπ» Author
Md. Sarowar Alam
Lead DevOps Engineer, Hogarth Worldwide
π§ Email: [email protected]
π LinkedIn: linkedin.com/in/sarowar