Skip to content

Latest commit

 

History

History
322 lines (238 loc) · 9.66 KB

File metadata and controls

322 lines (238 loc) · 9.66 KB

Deployment Plan for SDC Web Application

Overview

This document outlines the recommended deployment strategy for the SDC Web React application using Docker containerization.

Deployment Strategy: Multi-Stage Docker Build

We will use a multi-stage Docker build that:

  1. Builds the React application using Node.js
  2. Serves the built static files using Caddy web server
  3. Proxies API requests to the backend service

Why This Approach?

  • Small Image Size: Final image ~50MB (Caddy) vs ~1GB (Node.js)
  • Production Optimized: Caddy handles compression, caching, and HTTPS automatically
  • Reproducible: Same build process across all environments
  • Portable: Deploy anywhere Docker runs (AWS, GCP, DigitalOcean, self-hosted)
  • Backend Integration: Easy API proxying to backend service

Architecture

┌─────────────┐
│   Browser   │
└──────┬──────┘
       │
       ↓
┌─────────────────────────────┐
│  Frontend Container (Caddy) │
│  ┌────────────────────────┐ │
│  │  Static Files (React)  │ │  Port 80/443
│  └────────────────────────┘ │
│  ┌────────────────────────┐ │
│  │  Reverse Proxy         │ ├──→ /api/* requests
│  └────────────────────────┘ │
└──────────────┬──────────────┘
               │
               ↓
┌─────────────────────────────┐
│  Backend API Container      │  Port 8000
└─────────────────────────────┘

Files to Create

1. Dockerfile

Multi-stage build with:

  • Stage 1 (builder): Node.js to build the React app
  • Stage 2 (production): Caddy to serve static files

Key features:

  • Use .dockerignore to exclude unnecessary files
  • Install dependencies and build the app
  • Copy only the /dist folder to final image
  • Configure Caddy to serve SPA and proxy API
  • Extract version from package.json for image labeling

2. Caddyfile

Caddy configuration for:

  • Serving static files from /dist
  • SPA routing (handle client-side routes)
  • Reverse proxy /api/* to backend
  • Compression and caching headers
  • Optional HTTPS (auto with domain name)

3. .dockerignore

Exclude from Docker build context:

  • node_modules/
  • .git/
  • dist/
  • Development files (.env.local, etc.)
  • Documentation and test files

4. docker-compose.yml (Optional)

For local development/testing:

  • Frontend service (this React app)
  • Backend service (your API)
  • Shared network for service communication
  • Volume mounts for development

5. vite.config.ts Updates

Remove or make conditional:

  • Development-specific allowedHosts configuration
  • Add production-specific optimizations if needed

Docker Versioning Strategy

The Docker container version should be sourced from package.json to maintain consistency between the application version and container tags.

Version Extraction

Use the Node.js built-in require to extract the version:

# Extract version from package.json.
VERSION=$(node -p "require('./package.json').version")
echo $VERSION  # Output: 0.1.0

# Build with version tag.
docker build -t sdc-web:$VERSION -t sdc-web:latest .

This method:

  • Works cross-platform (no additional tools needed beyond Node.js)
  • Reads directly from package.json
  • Keeps version in a single source of truth

Tagging Strategy

Tag images with both specific version and latest:

VERSION=$(node -p "require('./package.json').version")
docker build -t sdc-web:$VERSION -t sdc-web:latest .

This creates two tags:

  • sdc-web:0.1.0 - Specific version for rollbacks and tracking
  • sdc-web:latest - Convenient tag for development/testing

Build & Deployment Instructions

Local Build & Test

# Build the Docker image with version from package.json.
VERSION=$(node -p "require('./package.json').version")
docker build -t sdc-web:$VERSION -t sdc-web:latest .

# Run the container (use specific version for clarity).
docker run -p 8080:80 \
  -e BACKEND_URL=http://backend:8000 \
  sdc-web:$VERSION

# Or use docker-compose.
docker-compose up

Production Deployment

Option A: Cloud Provider (AWS ECS, GCP Cloud Run, etc.)

# Extract version.
VERSION=$(node -p "require('./package.json').version")

# Tag for registry.
docker tag sdc-web:$VERSION registry.example.com/sdc-web:$VERSION
docker tag sdc-web:$VERSION registry.example.com/sdc-web:latest

# Push to registry.
docker push registry.example.com/sdc-web:$VERSION
docker push registry.example.com/sdc-web:latest

# Deploy using provider's CLI/console.

Option B: Self-Hosted with Docker

# Pull specific version and run on server.
VERSION=0.1.0  # Or extract from package.json
docker pull your-registry/sdc-web:$VERSION
docker run -d -p 80:80 -p 443:443 \
  --name sdc-web \
  -e BACKEND_URL=http://api.internal:8000 \
  your-registry/sdc-web:$VERSION

Option C: Kubernetes

# Deployment manifest
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sdc-web
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: sdc-web
        image: your-registry/sdc-web:0.1.0  # Use specific version, not latest
        ports:
        - containerPort: 80
        env:
        - name: BACKEND_URL
          value: "http://api-service:8000"

Environment Variables & Configuration

Build-time Variables (ARG)

  • NODE_VERSION: Node.js version for building (default: 20)
  • CADDY_VERSION: Caddy version for serving (default: 2-alpine)

Runtime Variables (ENV)

  • BACKEND_URL: Backend API URL (for Caddyfile proxy configuration)
  • PORT: Port to serve on (default: 80)

Backend API Configuration

The Caddyfile will proxy requests from /api/* to your backend. You need to:

  1. If backend is in same Docker network: Use service name (e.g., http://backend:8000)
  2. If backend is external: Use full URL (e.g., https://api.example.com)

Current Application Considerations

API Dependency

Your app uses:

  • RTK Query with base URL /api (src/redux/api.tsx:12)
  • Direct fetch API also uses /api prefix (src/api/fetchapi.tsx)

This means Caddy MUST proxy /api/* to your actual backend, or the app will break.

Vite Configuration

Current vite.config.ts has:

server: {
  allowedHosts: ["dfmoller-acer-aspire.springbok-cod.ts.net"],
}

This is development-only and should be:

  • Removed for production builds
  • Or made conditional based on environment

Security Considerations

  1. HTTPS: Caddy auto-provisions Let's Encrypt certs if you provide a domain
  2. API Keys: Never bake secrets into the Docker image. Use environment variables.
  3. CORS: If backend is on different domain, ensure CORS headers are configured
  4. Rate Limiting: Consider adding rate limiting in Caddy or at load balancer level

Performance Optimizations

  1. Compression: Caddy automatically gzips responses
  2. Caching: Set cache headers for static assets
  3. CDN: Consider putting CloudFlare or similar in front
  4. Multi-stage Build: Minimizes image size
  5. Health Checks: Add health check endpoint for orchestration

Next Steps After Implementation

  1. Test Build Locally: Ensure Docker image builds successfully
  2. Test with Backend: Verify API proxying works
  3. CI/CD Integration: Set up automated builds (GitHub Actions, GitLab CI, etc.)
  4. Monitoring: Add logging and monitoring (Prometheus, Datadog, etc.)
  5. Staging Environment: Deploy to staging before production
  6. Documentation: Update README with deployment instructions

Alternative Approaches Considered

Static Hosting (Netlify, Vercel, Cloudflare Pages)

Pros: Easy, cheap, CDN included Cons: API proxy more complex, requires public backend or serverless functions Verdict: Not ideal due to API dependency and desire for infrastructure control

Single-Stage Node.js Container

Pros: Simpler Dockerfile Cons: Much larger image (~1GB), not production-optimized for serving static files Verdict: Wasteful for a static SPA

nginx Instead of Caddy

Pros: More widely known, very mature Cons: More complex configuration, no auto-HTTPS Verdict: Caddy is simpler and more modern for this use case

Questions to Consider

  1. Where is your backend API?

    • Same server? Use Docker Compose with shared network
    • Different server? Configure BACKEND_URL environment variable
  2. Do you need HTTPS locally?

    • If yes, Caddy can handle it with self-signed certs
    • If no, keep it simple with HTTP on port 80
  3. What's your deployment target?

    • Cloud provider (AWS, GCP, Azure)?
    • Self-hosted VPS?
    • Kubernetes cluster?
  4. Do you need CI/CD?

    • GitHub Actions for automated builds?
    • GitLab CI/CD?
    • Jenkins?

Estimated Timeline

  • Create Dockerfile: 15 minutes
  • Create Caddyfile: 10 minutes
  • Create .dockerignore: 5 minutes
  • Update vite.config.ts: 5 minutes
  • Create docker-compose.yml: 10 minutes
  • Test build locally: 15 minutes
  • Documentation: 10 minutes

Total: ~1 hour for complete implementation and testing

References