Skip to content

Deployment

This guide covers the deployment process for all components of the LMS system.

Overview

The LMS consists of multiple deployable components:

  • Frontend - Static site deployment
  • Backend - Node.js API server
  • Cloud Functions - Cloudflare Workers
  • Database - PostgreSQL database
  • Documentation - VitePress static site

Prerequisites

  • Node.js 20+
  • pnpm package manager
  • PostgreSQL database
  • Cloudflare account (for Workers)
  • Hosting provider account (Vercel, Netlify, AWS, etc.)

Environment Variables

Frontend (.env.production)

env
VITE_API_URL=https://api.yourdomain.com
VITE_CLOUDFLARE_URL=https://functions.yourdomain.com

Backend (.env.production)

env
# Database
DATABASE_HOST=your-db-host.com
DATABASE_PORT=5432
DATABASE_USER=lms_user
DATABASE_PASSWORD=secure_password
DATABASE_NAME=lms_production
DATABASE_SSL=true

# JWT
JWT_SECRET=your-production-jwt-secret

# CORS
CORS_ORIGIN_1=https://yourdomain.com
CORS_ORIGIN_2=https://www.yourdomain.com

# Storage (Firebase)
FIREBASE_PROJECT_ID=your-project-id
FIREBASE_PRIVATE_KEY=your-private-key
FIREBASE_CLIENT_EMAIL=your-client-email

# Storage (Appwrite)
APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
APPWRITE_PROJECT_ID=your-project-id
APPWRITE_API_KEY=your-api-key

# AI (OpenAI)
OPENAI_API_KEY=your-openai-api-key

# Server
PORT=3000
NODE_ENV=production

Cloud Functions

bash
# Use wrangler to set secrets
wrangler secret put JWT_SECRET
wrangler secret put DATABASE_URL

Building for Production

Build All Packages

bash
# From root directory
pnpm build

This builds:

  • Frontend → apps/client-frontend/dist
  • Backend → apps/client-backend/dist
  • Cloud Functions → apps/cloud-functions/dist
  • Documentation → apps/docs/.vitepress/dist

Build Individual Components

bash
# Frontend
cd apps/client-frontend
pnpm build

# Backend
cd apps/client-backend
pnpm build

# Cloud Functions
cd apps/cloud-functions
pnpm deploy

# Documentation
cd apps/docs
pnpm build

Database Deployment

1. Provision PostgreSQL Database

Options:

  • Managed Services: AWS RDS, Azure Database, Google Cloud SQL
  • Database-as-a-Service: Supabase, Neon, Railway
  • Self-hosted: EC2, DigitalOcean Droplets

2. Run Migrations

bash
# Set production database URL
export DATABASE_URL="postgresql://user:pass@host:5432/lms_prod"

# Run migrations
cd apps/client-backend
pnpm typeorm migration:run

3. Seed Initial Data

bash
# Run seed script (if applicable)
node scripts/seed-production.js

Frontend Deployment

Vercel

bash
# Install Vercel CLI
pnpm add -g vercel

# Deploy
cd apps/client-frontend
vercel --prod

vercel.json:

json
{
  "buildCommand": "pnpm build",
  "outputDirectory": "dist",
  "framework": "vite",
  "env": {
    "VITE_API_URL": "@api-url"
  }
}

Netlify

bash
# Install Netlify CLI
pnpm add -g netlify-cli

# Deploy
cd apps/client-frontend
netlify deploy --prod

netlify.toml:

toml
[build]
  command = "pnpm build"
  publish = "dist"

[build.environment]
  NODE_VERSION = "20"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

AWS S3 + CloudFront

bash
# Build
pnpm build

# Upload to S3
aws s3 sync dist/ s3://your-bucket-name --delete

# Invalidate CloudFront cache
aws cloudfront create-invalidation \
  --distribution-id YOUR_DISTRIBUTION_ID \
  --paths "/*"

Docker

dockerfile
# Dockerfile
FROM node:20-alpine AS builder

WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install
COPY . .
RUN pnpm build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
bash
# Build and run
docker build -t lms-frontend .
docker run -p 80:80 lms-frontend

Backend Deployment

Heroku

bash
# Install Heroku CLI
npm install -g heroku

# Login
heroku login

# Create app
heroku create lms-api

# Set environment variables
heroku config:set DATABASE_URL=postgresql://...
heroku config:set JWT_SECRET=your-secret

# Deploy
git push heroku main

Procfile:

web: node dist/index.js

Railway

bash
# Install Railway CLI
npm install -g @railway/cli

# Login
railway login

# Deploy
railway up

railway.json:

json
{
  "build": {
    "builder": "NIXPACKS",
    "buildCommand": "pnpm build"
  },
  "deploy": {
    "startCommand": "node dist/index.js",
    "restartPolicyType": "ON_FAILURE"
  }
}

AWS EC2

bash
# Connect to instance
ssh -i key.pem ubuntu@your-instance-ip

# Install dependencies
sudo apt update
sudo apt install -y nodejs npm postgresql-client

# Clone repository
git clone https://github.com/your-repo/lms.git
cd lms

# Install pnpm
npm install -g pnpm

# Install dependencies
pnpm install

# Build
pnpm build

# Setup PM2
npm install -g pm2
pm2 start apps/client-backend/dist/index.js --name lms-api
pm2 save
pm2 startup

Docker

dockerfile
# Dockerfile
FROM node:20-alpine

WORKDIR /app

# Install pnpm
RUN npm install -g pnpm

# Copy package files
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY apps/client-backend/package.json ./apps/client-backend/
COPY packages/*/package.json ./packages/*/

# Install dependencies
RUN pnpm install --frozen-lockfile --prod

# Copy source
COPY . .

# Build
RUN pnpm build

# Expose port
EXPOSE 3000

# Start server
CMD ["node", "apps/client-backend/dist/index.js"]
bash
# Build and run
docker build -t lms-backend .
docker run -p 3000:3000 \
  -e DATABASE_URL=postgresql://... \
  -e JWT_SECRET=secret \
  lms-backend

Docker Compose

yaml
# docker-compose.yml
version: "3.8"

services:
  database:
    image: postgres:14
    environment:
      POSTGRES_USER: lms_user
      POSTGRES_PASSWORD: lms_password
      POSTGRES_DB: lms_prod
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  backend:
    build: ./apps/client-backend
    ports:
      - "3000:3000"
    environment:
      DATABASE_HOST: database
      DATABASE_PORT: 5432
      DATABASE_USER: lms_user
      DATABASE_PASSWORD: lms_password
      DATABASE_NAME: lms_prod
      JWT_SECRET: ${JWT_SECRET}
    depends_on:
      - database

  frontend:
    build: ./apps/client-frontend
    ports:
      - "80:80"
    environment:
      VITE_API_URL: http://localhost:3000
    depends_on:
      - backend

volumes:
  postgres_data:
bash
# Deploy
docker-compose up -d

Cloud Functions Deployment

Cloudflare Workers

bash
# Deploy to production
cd apps/cloud-functions
wrangler deploy

# Deploy to staging
wrangler deploy --env staging

wrangler.toml:

toml
name = "lms-cloud-functions"
main = "src/index.ts"
compatibility_date = "2024-01-01"

[env.production]
name = "lms-cloud-functions-prod"
routes = [
  { pattern = "functions.yourdomain.com/*", zone_name = "yourdomain.com" }
]

[env.staging]
name = "lms-cloud-functions-staging"
routes = [
  { pattern = "functions-staging.yourdomain.com/*", zone_name = "yourdomain.com" }
]
bash
# Set production secrets
wrangler secret put JWT_SECRET --env production
wrangler secret put DATABASE_URL --env production

Documentation Deployment

GitHub Pages

bash
# Build documentation
cd apps/docs
pnpm build

# Deploy to GitHub Pages
# (Usually automated via GitHub Actions)

GitHub Actions Workflow:

yaml
name: Deploy Docs

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: pnpm/action-setup@v2
      - uses: actions/setup-node@v3
        with:
          node-version: 20
          cache: "pnpm"

      - run: pnpm install
      - run: pnpm --filter docs build

      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: apps/docs/.vitepress/dist

Vercel/Netlify

Same as frontend deployment, pointing to apps/docs.

CI/CD Pipeline

GitHub Actions

yaml
# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: pnpm/action-setup@v2
      - uses: actions/setup-node@v3
        with:
          node-version: 20
          cache: "pnpm"

      - run: pnpm install
      - run: pnpm lint
      - run: pnpm test
      - run: pnpm build

  deploy-frontend:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: pnpm/action-setup@v2
      - uses: actions/setup-node@v3
        with:
          node-version: 20

      - run: pnpm install
      - run: pnpm --filter client-frontend build

      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          working-directory: ./apps/client-frontend

  deploy-backend:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: pnpm/action-setup@v2
      - uses: actions/setup-node@v3

      - run: pnpm install
      - run: pnpm --filter client-backend build

      - name: Deploy to Railway
        run: |
          npm install -g @railway/cli
          railway up --service backend
        env:
          RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}

  deploy-cloudflare:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: pnpm/action-setup@v2
      - uses: actions/setup-node@v3

      - run: pnpm install

      - name: Deploy to Cloudflare Workers
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          workingDirectory: apps/cloud-functions

Monitoring

Application Monitoring

  • Sentry - Error tracking
  • DataDog - APM and logging
  • New Relic - Performance monitoring

Infrastructure Monitoring

  • CloudWatch (AWS)
  • Azure Monitor (Azure)
  • Google Cloud Monitoring (GCP)

Uptime Monitoring

  • UptimeRobot
  • Pingdom
  • StatusCake

SSL/TLS Certificates

Let's Encrypt (Free)

bash
# Install Certbot
sudo apt install certbot python3-certbot-nginx

# Obtain certificate
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

# Auto-renewal
sudo certbot renew --dry-run

Cloudflare SSL

  1. Add domain to Cloudflare
  2. Enable "Full (strict)" SSL mode
  3. Certificates automatically managed

Performance Optimization

Frontend

  • Enable gzip/brotli compression
  • Implement CDN caching
  • Optimize images
  • Code splitting
  • Lazy loading

Backend

  • Database connection pooling
  • Redis caching
  • Rate limiting
  • API response compression

CDN

  • CloudFlare
  • AWS CloudFront
  • Fastly

Backup Strategy

Database Backups

bash
# Automated daily backup
0 2 * * * pg_dump -h host -U user -d lms_prod > backup-$(date +\%Y\%m\%d).sql

# Backup to S3
aws s3 cp backup.sql s3://backups/lms/$(date +\%Y\%m\%d)/

File Storage Backups

  • Enable versioning on S3/Firebase Storage
  • Regular snapshots
  • Offsite backups

Rollback Strategy

Frontend

bash
# Revert to previous deployment
vercel rollback
# or
netlify rollback

Backend

bash
# Using PM2
pm2 stop lms-api
git checkout previous-commit
pnpm build
pm2 restart lms-api

# Using Docker
docker pull lms-backend:previous-tag
docker-compose up -d

Database

bash
# Revert migration
typeorm migration:revert

Health Checks

Backend Health Endpoint

typescript
app.get("/health", (req, res) => {
  res.json({
    status: "ok",
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
  });
});

Kubernetes Liveness Probe

yaml
livenessProbe:
  httpGet:
    path: /health
    port: 3000
  initialDelaySeconds: 30
  periodSeconds: 10

Scaling

Horizontal Scaling

  • Load balancer (Nginx, HAProxy, AWS ALB)
  • Multiple backend instances
  • Session management (Redis)
  • Database read replicas

Vertical Scaling

  • Increase server resources
  • Optimize database queries
  • Improve caching

Security Checklist

  • [ ] HTTPS enabled
  • [ ] Environment variables secured
  • [ ] Database credentials rotated
  • [ ] API rate limiting enabled
  • [ ] CORS properly configured
  • [ ] Security headers set
  • [ ] Input validation implemented
  • [ ] SQL injection prevention
  • [ ] XSS protection
  • [ ] CSRF protection

Post-Deployment

  1. Verify deployments - Check all services are running
  2. Run smoke tests - Test critical functionality
  3. Monitor logs - Watch for errors
  4. Check metrics - Verify performance
  5. Update documentation - Document any changes

Troubleshooting

Frontend not loading

  • Check API URL configuration
  • Verify CORS settings
  • Check browser console for errors

Backend crashes

  • Check logs: pm2 logs lms-api
  • Verify environment variables
  • Check database connectivity

Database connection errors

  • Verify credentials
  • Check firewall rules
  • Ensure SSL settings match