Deployment Overview

WI-CARPOOL is a React-based single-page application (SPA) that can be deployed to various hosting platforms. This guide covers multiple deployment strategies from simple static hosting to advanced server configurations.

🌐 Static Hosting

Deploy to CDN-based platforms like Netlify, Vercel, or AWS S3 for optimal performance.

🖥️ VPS/Dedicated Server

Full control deployment with Nginx, Apache, or custom server configurations.

🐳 Docker Container

Containerized deployment for consistent environments across different platforms.

☁️ Cloud Platforms

Deploy to AWS, Google Cloud, or Azure with auto-scaling and managed services.

Build Preparation

1. Environment Configuration

Before building for production, configure your environment variables and constants:

Update API Constants (src/api/constants.ts)

export const AppConstants = {
  baseUrl: "https://your-api-domain.com/api/",
  // Update all API endpoints to production URLs
  
  // Image and asset paths
  imageBaseUrl: "https://your-cdn-domain.com/",
  
  // External service configurations
  googleMapsApiKey: "your-production-google-maps-key",
  
  // Payment gateway configurations
  paymentGatewayUrl: "https://your-payment-gateway.com/",
};

Update Vite Configuration (vite.config.ts)

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import path from "path";

export default defineConfig({
  server: {
    host: "::",
    port: 8080,
  },
  plugins: [react()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
  build: {
    outDir: "dist",
    sourcemap: false, // Set to true for debugging
    minify: 'terser',
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          router: ['react-router-dom'],
          ui: ['@tanstack/react-query'],
        },
      },
    },
    terserOptions: {
      compress: {
        drop_console: true, // Remove console.log in production
        drop_debugger: true,
      },
    },
  },
  base: "/", // Update if deploying to subdirectory
});

2. Production Build

Create Production Build

# Install dependencies
npm ci --only=production

# Create optimized production build
npm run build

# Verify build output
ls -la dist/

# Test production build locally (optional)
npm run preview
Build Output: The production build will be created in the dist/ directory. This contains all static files needed for deployment.

Static Hosting Deployment

Netlify Deployment

Netlify offers seamless deployment with automatic builds from Git repositories.

1. Manual Deployment

# Build the project
npm run build

# Install Netlify CLI
npm install -g netlify-cli

# Login to Netlify
netlify login

# Deploy to Netlify
netlify deploy --prod --dir=dist

2. Continuous Deployment (Recommended)

# Create netlify.toml in project root
[build]
  publish = "dist"
  command = "npm run build"

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

[build.environment]
  NODE_VERSION = "18"

Vercel Deployment

Using Vercel CLI

# Install Vercel CLI
npm install -g vercel

# Login to Vercel
vercel login

# Deploy to Vercel
vercel --prod

# Configure project settings in vercel.json
{
  "builds": [
    {
      "src": "package.json",
      "use": "@vercel/static-build",
      "config": {
        "distDir": "dist"
      }
    }
  ],
  "routes": [
    {
      "handle": "filesystem"
    },
    {
      "src": "/(.*)",
      "dest": "/index.html"
    }
  ]
}

AWS S3 + CloudFront

S3 Bucket Configuration

# Install AWS CLI
aws configure

# Create S3 bucket
aws s3 mb s3://your-wicarpool-bucket --region us-east-1

# Enable static website hosting
aws s3 website s3://your-wicarpool-bucket \
    --index-document index.html \
    --error-document index.html

# Upload build files
aws s3 sync dist/ s3://your-wicarpool-bucket \
    --delete \
    --cache-control max-age=31536000 \
    --exclude "*.html" \
    --exclude "service-worker.js"

# Upload HTML and service worker with shorter cache
aws s3 sync dist/ s3://your-wicarpool-bucket \
    --delete \
    --cache-control max-age=0 \
    --include "*.html" \
    --include "service-worker.js"

CloudFront Distribution

# Create CloudFront distribution configuration
{
  "CallerReference": "wicarpool-$(date +%s)",
  "Origins": {
    "Quantity": 1,
    "Items": [
      {
        "Id": "S3-wicarpool",
        "DomainName": "your-wicarpool-bucket.s3.amazonaws.com",
        "S3OriginConfig": {
          "OriginAccessIdentity": ""
        }
      }
    ]
  },
  "DefaultCacheBehavior": {
    "TargetOriginId": "S3-wicarpool",
    "ViewerProtocolPolicy": "redirect-to-https",
    "MinTTL": 0,
    "ForwardedValues": {
      "QueryString": false,
      "Cookies": {"Forward": "none"}
    }
  },
  "CustomErrorResponses": {
    "Quantity": 1,
    "Items": [
      {
        "ErrorCode": 404,
        "ResponsePagePath": "/index.html",
        "ResponseCode": "200"
      }
    ]
  },
  "Enabled": true,
  "PriceClass": "PriceClass_100"
}

Server Deployment

Nginx Configuration

Deploy to a VPS or dedicated server using Nginx as a web server.

Install Nginx (Ubuntu/Debian)

# Update package list
sudo apt update

# Install Nginx
sudo apt install nginx

# Start and enable Nginx
sudo systemctl start nginx
sudo systemctl enable nginx

# Check status
sudo systemctl status nginx

Nginx Site Configuration

# Create site configuration: /etc/nginx/sites-available/wicarpool
server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    root /var/www/wicarpool/dist;
    index index.html;

    # Enable gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_proxied expired no-cache no-store private must-revalidate auth;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/javascript
        application/xml+rss
        application/json;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;
    add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;

    # Static file caching
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        try_files $uri =404;
    }

    # HTML files - no caching
    location ~* \.html$ {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";
        add_header Expires "0";
        try_files $uri /index.html;
    }

    # Service Worker - no caching
    location = /sw.js {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";
        add_header Expires "0";
        try_files $uri =404;
    }

    # React Router - all routes should serve index.html
    location / {
        try_files $uri $uri/ /index.html;
    }

    # API proxy (if backend is on same server)
    location /api/ {
        proxy_pass http://localhost:3001/;
        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;
    }

    # Health check endpoint
    location /health {
        access_log off;
        return 200 "healthy\n";
        add_header Content-Type text/plain;
    }
}

# Enable the site
sudo ln -s /etc/nginx/sites-available/wicarpool /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Apache Configuration

Apache Virtual Host

# Create virtual host: /etc/apache2/sites-available/wicarpool.conf

    ServerName yourdomain.com
    ServerAlias www.yourdomain.com
    DocumentRoot /var/www/wicarpool/dist
    
    # Enable mod_rewrite for React Router
    RewriteEngine On
    RewriteRule ^index\.html$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.html [L]
    
    # Enable compression
    LoadModule deflate_module modules/mod_deflate.so
    
        SetOutputFilter DEFLATE
        SetEnvIfNoCase Request_URI \
            \.(?:gif|jpe?g|png)$ no-gzip dont-vary
        SetEnvIfNoCase Request_URI \
            \.(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary
    
    
    # Cache static files
    
        ExpiresActive On
        ExpiresDefault "access plus 1 year"
    
    
    ErrorLog ${APACHE_LOG_DIR}/wicarpool_error.log
    CustomLog ${APACHE_LOG_DIR}/wicarpool_access.log combined


# Enable the site
sudo a2ensite wicarpool.conf
sudo a2enmod rewrite
sudo a2enmod expires
sudo a2enmod deflate
sudo systemctl restart apache2

Deployment Script

Automated Deployment Script (deploy.sh)

#!/bin/bash

# WI-CARPOOL Deployment Script
set -e

# Configuration
PROJECT_DIR="/var/www/wicarpool"
BACKUP_DIR="/var/backups/wicarpool"
GIT_REPO="https://github.com/your-username/wi-carpool-frontend.git"
BRANCH="main"

echo "Starting WI-CARPOOL deployment..."

# Create backup
echo "Creating backup..."
sudo mkdir -p $BACKUP_DIR
sudo cp -r $PROJECT_DIR $BACKUP_DIR/$(date +%Y%m%d_%H%M%S)

# Navigate to project directory
cd $PROJECT_DIR

# Pull latest changes
echo "Pulling latest changes..."
sudo git fetch origin
sudo git checkout $BRANCH
sudo git pull origin $BRANCH

# Install dependencies
echo "Installing dependencies..."
sudo npm ci --only=production

# Build application
echo "Building application..."
sudo npm run build

# Set proper permissions
echo "Setting permissions..."
sudo chown -R www-data:www-data $PROJECT_DIR/dist
sudo chmod -R 755 $PROJECT_DIR/dist

# Test Nginx configuration
echo "Testing Nginx configuration..."
sudo nginx -t

# Reload Nginx
echo "Reloading Nginx..."
sudo systemctl reload nginx

# Clean up old builds (keep last 5 backups)
echo "Cleaning up old backups..."
sudo find $BACKUP_DIR -maxdepth 1 -type d -name "20*" | sort -r | tail -n +6 | sudo xargs rm -rf

echo "Deployment completed successfully!"
echo "Application is available at: http://yourdomain.com"

Docker Deployment

Dockerfile

Multi-stage Dockerfile

# Build stage
FROM node:18-alpine as build

WORKDIR /app

# Copy package files
COPY package*.json ./
COPY tsconfig*.json ./
COPY vite.config.ts ./

# Install dependencies
RUN npm ci --only=production

# Copy source code
COPY src/ ./src/
COPY public/ ./public/
COPY index.html ./

# Build application
RUN npm run build

# Production stage
FROM nginx:alpine

# Copy custom nginx configuration
COPY nginx.conf /etc/nginx/nginx.conf

# Copy built application
COPY --from=build /app/dist /usr/share/nginx/html

# Add health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:80/health || exit 1

# Expose port
EXPOSE 80

# Start nginx
CMD ["nginx", "-g", "daemon off;"]

Nginx Configuration for Docker (nginx.conf)

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    
    access_log /var/log/nginx/access.log main;
    
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    
    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/javascript
        application/xml+rss
        application/json;
    
    server {
        listen 80;
        server_name _;
        root /usr/share/nginx/html;
        index index.html;
        
        # Security headers
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-XSS-Protection "1; mode=block" always;
        add_header X-Content-Type-Options "nosniff" always;
        
        # Static files caching
        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
            expires 1y;
            add_header Cache-Control "public, immutable";
        }
        
        # React Router
        location / {
            try_files $uri $uri/ /index.html;
        }
        
        # Health check
        location /health {
            access_log off;
            return 200 "healthy\n";
            add_header Content-Type text/plain;
        }
    }
}

Docker Compose (docker-compose.yml)

version: '3.8'

services:
  wicarpool-frontend:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: wicarpool-frontend
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./ssl:/etc/nginx/ssl:ro  # For SSL certificates
    environment:
      - NODE_ENV=production
    networks:
      - wicarpool-network
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.wicarpool.rule=Host(`yourdomain.com`)"
      - "traefik.http.routers.wicarpool.tls.certresolver=letsencrypt"

networks:
  wicarpool-network:
    driver: bridge

# Optional: Add reverse proxy
  traefik:
    image: traefik:v2.9
    container_name: traefik
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik:/etc/traefik:ro
      - ./ssl:/etc/ssl:ro
    networks:
      - wicarpool-network

Build and Deploy with Docker

# Build the Docker image
docker build -t wicarpool-frontend .

# Run the container
docker run -d \
  --name wicarpool-frontend \
  --restart unless-stopped \
  -p 80:80 \
  wicarpool-frontend

# Using Docker Compose
docker-compose up -d

# Check container status
docker ps
docker logs wicarpool-frontend

# Update deployment
docker-compose pull
docker-compose up -d --force-recreate

Cloud Platform Deployment

Google Cloud Platform

Deploy to Google Cloud Storage

# Install Google Cloud SDK
curl https://sdk.cloud.google.com | bash
exec -l $SHELL
gcloud init

# Create a bucket
gsutil mb gs://your-wicarpool-bucket

# Configure bucket for static website hosting
gsutil web set -m index.html -e index.html gs://your-wicarpool-bucket

# Upload build files
gsutil -m cp -r dist/* gs://your-wicarpool-bucket

# Set public access
gsutil -m acl ch -r -u AllUsers:R gs://your-wicarpool-bucket

# Configure load balancer and CDN through Cloud Console

Google Cloud Run

Deploy containerized application

# Build and push to Google Container Registry
gcloud builds submit --tag gcr.io/PROJECT_ID/wicarpool-frontend

# Deploy to Cloud Run
gcloud run deploy wicarpool-frontend \
  --image gcr.io/PROJECT_ID/wicarpool-frontend \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated \
  --port 80

AWS Elastic Beanstalk

Deploy using EB CLI

# Install EB CLI
pip install awsebcli

# Initialize Elastic Beanstalk application
eb init

# Create environment and deploy
eb create wicarpool-production

# Deploy updates
eb deploy

# Configuration file: .ebextensions/nginx.config
files:
  "/etc/nginx/conf.d/https_redirect.conf":
    mode: "000644"
    owner: root
    group: root
    content: |
      server {
        listen 8080;
        return 301 https://$host$request_uri;
      }

SSL/HTTPS Setup

Let's Encrypt (Certbot)

Install and configure SSL certificates

# Install Certbot (Ubuntu/Debian)
sudo apt install certbot python3-certbot-nginx

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

# Test automatic renewal
sudo certbot renew --dry-run

# Auto-renewal cron job
echo "0 12 * * * /usr/bin/certbot renew --quiet" | sudo crontab -

Custom SSL Certificate

Configure custom SSL certificates

# Update Nginx configuration for SSL
server {
    listen 443 ssl http2;
    server_name yourdomain.com www.yourdomain.com;
    root /var/www/wicarpool/dist;
    
    # SSL configuration
    ssl_certificate /path/to/your/certificate.crt;
    ssl_certificate_key /path/to/your/private.key;
    ssl_session_timeout 1d;
    ssl_session_cache shared:MozTLS:10m;
    ssl_session_tickets off;
    
    # Modern configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    
    # HSTS
    add_header Strict-Transport-Security "max-age=63072000" always;
    
    # ... rest of configuration
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

CI/CD Pipeline

GitHub Actions

Deployment workflow (.github/workflows/deploy.yml)

name: Deploy WI-CARPOOL

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run tests
      run: npm test
    
    - name: Build application
      run: npm run build
    
    - name: Upload build artifacts
      uses: actions/upload-artifact@v3
      with:
        name: build-files
        path: dist/

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - name: Download build artifacts
      uses: actions/download-artifact@v3
      with:
        name: build-files
        path: dist/
    
    # Deploy to your server
    - name: Deploy to server
      uses: appleboy/[email protected]
      with:
        host: ${{ secrets.HOST }}
        username: ${{ secrets.USERNAME }}
        key: ${{ secrets.SSH_KEY }}
        script: |
          cd /var/www/wicarpool
          git pull origin main
          npm ci --only=production
          npm run build
          sudo systemctl reload nginx

GitLab CI/CD

GitLab pipeline (.gitlab-ci.yml)

stages:
  - test
  - build
  - deploy

variables:
  NODE_VERSION: "18"

test:
  stage: test
  image: node:$NODE_VERSION-alpine
  cache:
    paths:
      - node_modules/
  script:
    - npm ci
    - npm run test
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 hour

deploy_production:
  stage: deploy
  image: alpine:latest
  before_script:
    - apk add --no-cache rsync openssh
    - mkdir -p ~/.ssh
    - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
    - ssh-keyscan -H $SERVER_HOST >> ~/.ssh/known_hosts
  script:
    - rsync -avz --delete dist/ $SERVER_USER@$SERVER_HOST:/var/www/wicarpool/dist/
    - ssh $SERVER_USER@$SERVER_HOST "sudo systemctl reload nginx"
  only:
    - main
  when: manual

Post-Deployment Monitoring

Health Checks

Simple monitoring script (monitor.sh)

#!/bin/bash

URL="https://yourdomain.com"
EMAIL="[email protected]"

# Check if site is accessible
if curl -f -s "$URL" > /dev/null; then
    echo "$(date): Site is UP"
else
    echo "$(date): Site is DOWN"
    echo "WI-CARPOOL site is down!" | mail -s "Site Alert" "$EMAIL"
fi

# Check SSL certificate expiry
SSL_EXPIRY=$(echo | openssl s_client -servername yourdomain.com -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -dates | grep notAfter | cut -d= -f2)
SSL_EXPIRY_EPOCH=$(date -d "$SSL_EXPIRY" +%s)
CURRENT_EPOCH=$(date +%s)
DAYS_UNTIL_EXPIRY=$(( (SSL_EXPIRY_EPOCH - CURRENT_EPOCH) / 86400 ))

if [ $DAYS_UNTIL_EXPIRY -lt 30 ]; then
    echo "SSL certificate expires in $DAYS_UNTIL_EXPIRY days" | mail -s "SSL Alert" "$EMAIL"
fi

Performance Monitoring

  • Google Analytics - Track user behavior and site performance
  • Google PageSpeed Insights - Monitor page load speeds
  • Uptime monitoring - Services like UptimeRobot or Pingdom
  • Error tracking - Services like Sentry for frontend error monitoring

Deployment Troubleshooting

Common Issues

404 Errors on Refresh

Problem: React Router routes return 404 when accessed directly
Solution: Configure server to serve index.html for all routes (shown in Nginx/Apache configs above)

Build Failures

Debug build issues

# Clear node_modules and reinstall
rm -rf node_modules package-lock.json
npm install

# Build with verbose output
npm run build -- --verbose

# Check for TypeScript errors
npx tsc --noEmit

Performance Issues

Optimize build

# Analyze bundle size
npm install -g webpack-bundle-analyzer
npx webpack-bundle-analyzer dist/static/js/*.js

# Enable source maps for debugging
# In vite.config.ts:
build: {
  sourcemap: true
}

Next Steps