Deployment Guide
Complete guide for deploying WI-CARPOOL to production servers with various hosting options
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
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
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
}