Skip to content

Local Production-Like Environment Setup

This guide helps developers set up a local environment that mirrors the production deployment, allowing you to test features in a production-like setup before deploying.

Overview

The local production environment includes all the same services as production:

  • Backend API: Node.js/Fastify application
  • Frontend: React/Vite application
  • Database: PostgreSQL
  • Reverse Proxy: Nginx
  • Monitoring: Prometheus and Grafana
  • Container Management: Portainer
  • Database Management: Adminer
  • Metrics Collection: cAdvisor and Node Exporter

Prerequisites

  • Docker and Docker Compose installed
  • Git
  • At least 8GB RAM available for containers
  • Ports 80, 443, 3000, 4000, 5432, 9001, 9090 available

Quick Start

  1. Clone the repositories:

    Bash
    1
    2
    3
    4
    5
    6
    7
    # Clone the main PRS repositories
    git clone https://gitlab.stratpoint.dev/cityland/prs-backend
    git clone https://gitlab.stratpoint.dev/cityland/prs-frontend
    
    # Clone the deployment repository
    git clone https://gitlab.stratpoint.dev/cityland/deployment
    cd deployment
    

  2. Set up the local development environment:

    Bash
    1
    2
    3
    4
    5
    6
    7
    # Create local configuration directory
    mkdir -p local
    
    # Copy and customize environment files
    cp .env.example local/.env.local
    cp backend.env local/backend.local.env
    cp frontend.env local/frontend.local.env
    

  3. Configure environment variables (see Configuration section below)

  4. Start the environment:

    Bash
    ./deploy-local.sh --build --up --init-db
    

  5. Access the applications:

  6. PRS Application: http://localhost
  7. Grafana: http://localhost:3000 (admin/admin)
  8. Prometheus: http://localhost:9090
  9. Portainer: http://localhost:9001
  10. Adminer: http://localhost:8080

Directory Structure

Text Only
# Repository structure
├── deployment/                   # Deployment repository
│   ├── local/                   # Local development configurations
│   │   ├── compose.local.yml    # Local Docker Compose configuration
│   │   ├── .env.local          # Local environment variables
│   │   ├── backend.local.env   # Local backend environment
│   │   ├── frontend.local.env  # Local frontend environment
│   │   └── deploy-local.sh     # Local deployment script
│   ├── nginx/
│   │   └── local/              # Local Nginx configurations
│   │       └── default.conf    # Local Nginx configuration
│   ├── prometheus/
│   │   └── local/              # Local Prometheus configurations
│   │       └── prometheus.yml  # Local Prometheus configuration
│   ├── grafana/
│   │   └── local/              # Local Grafana configurations
│   │       ├── datasources/    # Grafana data sources
│   │       └── dashboards/     # Grafana dashboards
├── prs-backend/                 # Backend repository
└── prs-frontend/                # Frontend repository

Configuration

Environment Variables (.env.local)

Bash
# Database Configuration
POSTGRES_DB=prs_local
POSTGRES_USER=admin
POSTGRES_PASSWORD=admin123



# API URLs
VITE_APP_API_URL=http://localhost/api

# Cloudflare (disabled for local)
CLOUDFLARE_TUNNEL_TOKEN=disabled

# Monitoring
GRAFANA_ADMIN_PASSWORD=admin

Backend Configuration (backend.local.env)

Bash
# Application config
PORT=4000
HOST=0.0.0.0
JWT_SECRET=local-jwt-secret-key-for-development
NODE_ENV=development
OTP_KEY=U29tZVNlY3JldEtleVdpdGg2NEJ5dGVz
PASS_SECRET=12345
BYPASS_OTP=true
LOG_LEVEL=debug
DISABLE_REQUEST_LOGS=false

# Database configs
POSTGRES_SERVICE=postgres
POSTGRES_HOST=postgres
POSTGRES_DB=prs_local
POSTGRES_PORT=5432
POSTGRES_USER=admin
POSTGRES_PASSWORD=admin123
DIALECT=postgres
POOL_MIN=0
POOL_MAX=5
POOL_ACQUIRE=30000
POOL_IDLE=10000
POOL_EVICTION=20000
DISABLE_SSL=true



# Root User
ROOT_USER_NAME=admin
ROOT_USER_EMAIL=admin@local.dev
ROOT_USER_PASSWORD=admin123

# API Integration (Mock endpoints for local development)
CITYLAND_API_URL=https://cmd-test.free.beeceptor.com
CITYLAND_ACCOUNTING_URL=https://cityland-accounting.free.beeceptor.com

# Department Association
ASSOCIATION_DEPARTMENT_CODE=10

# PLG Stack Integration
PROMETHEUS_ENABLED=true

# Development Features
ENABLE_DEBUG_ROUTES=true
ENABLE_SWAGGER=true
CORS_ORIGIN=http://localhost:3000,http://localhost

Frontend Configuration (frontend.local.env)

Bash
# API Configuration
VITE_APP_API_URL=http://localhost/api
VITE_APP_UPLOAD_URL=http://localhost/api/upload

# Development Features
VITE_APP_ENABLE_API_MOCKING=false
VITE_APP_ENABLE_DEVTOOLS=true

# PLG Stack Integration
VITE_APP_PROMETHEUS_GATEWAY=http://localhost:9091
VITE_APP_GRAFANA_URL=http://localhost:3000

# Logging Configuration
VITE_APP_LOG_LEVEL=debug
VITE_APP_LOG_BATCH_SIZE=10
VITE_APP_LOG_FLUSH_INTERVAL=5000

# Application Metadata
VITE_APP_APP_VERSION=1.0.0-local
VITE_APP_BUILD_ID=local-development
VITE_APP_ENVIRONMENT=local

Services Configuration

Local Docker Compose (compose.local.yml)

The local compose file includes all production services with development-friendly configurations:

  • Development volumes: Source code mounted for hot reloading
  • Debug ports: Additional ports exposed for debugging
  • Development images: Images optimized for development
  • Local networking: Simplified networking for local access
  • Debug logging: Enhanced logging for development

Nginx Configuration (nginx/local/default.conf)

Local Nginx configuration includes:

  • Frontend serving: Serves React development build
  • API proxying: Proxies API requests to backend
  • Static assets: Serves uploaded files and assets
  • CORS headers: Proper CORS configuration for development
  • Debug headers: Additional headers for debugging

Monitoring Stack

Prometheus (prometheus/local/prometheus.yml)

  • Local targets: All services configured for local access
  • Short intervals: Faster scraping for development
  • Debug metrics: Additional metrics for development

Grafana (grafana/local/)

  • Pre-configured dashboards: PRS-specific dashboards
  • Local data sources: Prometheus and Loki configured
  • Development panels: Additional panels for debugging

Development Workflow

Starting the Environment

Bash
1
2
3
4
5
6
# Full environment startup
./deploy-local.sh --build --up --init-db

# Individual service management
./deploy-local.sh --restart backend
./deploy-local.sh --logs frontend

Development Commands

Bash
# Database operations
./deploy-local.sh --migrate        # Run migrations
./deploy-local.sh --seed          # Run seeders
./deploy-local.sh --reset-db      # Reset database

# Monitoring
./deploy-local.sh --logs          # View all logs
./deploy-local.sh --logs backend  # View backend logs
./deploy-local.sh --status        # Check service status

# Cleanup
./deploy-local.sh --clean         # Clean Docker resources
./deploy-local.sh --down          # Stop all services

Hot Reloading

The local environment supports hot reloading for both frontend and backend:

  • Frontend: Vite dev server with HMR
  • Backend: Nodemon for automatic restarts
  • Nginx: Configuration reloading without restart

Debugging

Backend Debugging

Bash
1
2
3
4
5
# Enable debug mode
docker compose -f compose.local.yml exec backend npm run debug

# View debug logs
docker compose -f compose.local.yml logs -f backend

Frontend Debugging

Bash
1
2
3
4
5
6
# Access development tools
# Open http://localhost:3000 in browser
# React DevTools and Redux DevTools available

# View frontend logs
docker compose -f compose.local.yml logs -f frontend

Database Management

Using Adminer

  1. Open http://localhost:8080
  2. Server: postgres
  3. Username: admin
  4. Password: admin123
  5. Database: prs_local

Direct Database Access

Bash
1
2
3
4
5
6
7
8
# Connect to PostgreSQL
docker compose -f compose.local.yml exec postgres psql -U admin -d prs_local

# Backup database
./deploy-local.sh --backup-db

# Restore database
./deploy-local.sh --restore-db backup_file.sql

Monitoring and Observability

Grafana Dashboards

Pre-configured dashboards available:

  1. PRS Application Overview
  2. Request rates and response times
  3. Error rates and success rates
  4. User activity and business metrics

  5. Infrastructure Monitoring

  6. Container resource usage
  7. Database performance

  8. Business Intelligence

  9. Requisition workflow metrics
  10. User activity patterns
  11. System usage analytics

Prometheus Metrics

Key metrics available:

  • Application Metrics: Request/response metrics, business events
  • Infrastructure Metrics: CPU, memory, disk usage
  • Database Metrics: Query performance, connection pools

Testing Production Features

Load Testing

Bash
1
2
3
4
5
# Install k6 for load testing
npm install -g k6

# Run load tests
k6 run scripts/load-test.js

Performance Testing

Bash
1
2
3
4
5
6
# Monitor performance metrics
# Open Grafana: http://localhost:3000
# View "Performance Testing" dashboard

# Run performance tests
npm run test:performance

Security Testing

Bash
1
2
3
4
5
6
7
8
# Test authentication flows
npm run test:auth

# Test authorization
npm run test:permissions

# Test input validation
npm run test:validation

Troubleshooting

Common Issues

Port Conflicts

Bash
1
2
3
4
5
6
# Check port usage
netstat -tulpn | grep :80

# Stop conflicting services
sudo systemctl stop apache2
sudo systemctl stop nginx

Memory Issues

Bash
1
2
3
4
5
# Check Docker memory usage
docker stats

# Increase Docker memory limit
# Docker Desktop: Settings > Resources > Memory

Database Connection Issues

Bash
1
2
3
4
5
6
7
# Check database status
docker compose -f compose.local.yml ps postgres

# Reset database
./deploy-local.sh --down
docker volume rm deployment_postgres_data
./deploy-local.sh --up --init-db

Service Discovery Issues

Bash
1
2
3
4
5
6
7
# Check network connectivity
docker compose -f compose.local.yml exec backend ping postgres
docker compose -f compose.local.yml exec frontend ping backend

# Restart networking
./deploy-local.sh --down
./deploy-local.sh --up

Log Analysis

Bash
1
2
3
4
5
6
7
8
# View all service logs
./deploy-local.sh --logs

# Filter logs by service
./deploy-local.sh --logs backend | grep ERROR

# View real-time logs
docker compose -f compose.local.yml logs -f --tail=100

Performance Debugging

Bash
1
2
3
4
5
6
7
8
# Check resource usage
docker stats

# Profile backend performance
docker compose -f compose.local.yml exec backend npm run profile

# Monitor database queries
docker compose -f compose.local.yml logs postgres | grep "duration:"

Production Parity

This local environment maintains parity with production in:

  • Service Architecture: Same services and configurations
  • Data Flow: Identical request/response patterns
  • Security: Same authentication and authorization
  • Monitoring: Same observability stack
  • Performance: Similar performance characteristics

Differences from Production

  • SSL/TLS: HTTP instead of HTTPS for simplicity
  • Scaling: Single instance instead of multiple replicas
  • Storage: Local volumes instead of cloud storage
  • Networking: Simplified networking without load balancers
  • Security: Relaxed security for development convenience

Next Steps

After setting up the local environment:

  1. Explore the Application: Test all features in the production-like environment
  2. Monitor Performance: Use Grafana dashboards to understand system behavior
  3. Test Integrations: Verify external API integrations work correctly
  4. Load Testing: Run load tests to understand system limits
  5. Deploy to Production: Use the same configurations for production deployment

For production deployment, refer to the Cityland Deployment Repository which contains the complete production deployment configuration.

Local Development Files

Docker Compose Configuration (compose.local.yml)

YAML
services:
  # Backend API (Development Mode)
  backend:
    build:
      context: ../prs-backend
      dockerfile: Dockerfile.dev
      target: development
    image: prs-backend:local
    restart: unless-stopped
    depends_on:
      - postgres
    env_file:
      - ./local/backend.local.env
    volumes:
      - ../prs-backend:/usr/app
      - /usr/app/node_modules
      - backend_logs:/usr/app/logs
      - ../prs-backend/upload:/usr/app/upload
    ports:
      - "4000:4000"
      - "9229:9229"  # Debug port
    networks:
      - app_network
      - monitoring_network
    command: npm run dev

  # Frontend (Development Mode)
  frontend:
    build:
      context: ../prs-frontend
      dockerfile: Dockerfile.dev
      target: development
    image: prs-frontend:local
    restart: unless-stopped
    env_file:
      - ./local/frontend.local.env
    volumes:
      - ../prs-frontend:/usr/app
      - /usr/app/node_modules
    ports:
      - "3000:3000"
      - "24678:24678"  # Vite HMR port
    networks:
      - app_network
    command: npm run dev

  # Database
  postgres:
    image: postgres:13
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB:-prs_local}
      POSTGRES_USER: ${POSTGRES_USER:-admin}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-admin123}
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./local/postgres/init:/docker-entrypoint-initdb.d
    networks:
      - app_network



  # Nginx Reverse Proxy
  nginx:
    image: nginx:latest
    restart: unless-stopped
    ports:
      - "80:80"
    volumes:
      - ./nginx/local/default.conf:/etc/nginx/conf.d/default.conf
      - ../prs-backend/upload:/usr/share/nginx/html/upload
    depends_on:
      - backend
      - frontend
    networks:
      - app_network
      - monitoring_network





  # Prometheus for metrics
  prometheus:
    image: prom/prometheus:latest
    restart: unless-stopped
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/local/prometheus.yml:/etc/prometheus/prometheus.yml
      - ./prometheus/local/alerts:/etc/prometheus/alerts
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
      - '--web.enable-lifecycle'
      - '--web.enable-admin-api'
      - '--storage.tsdb.retention.time=7d'
    networks:
      - monitoring_network

  # Grafana for visualization
  grafana:
    image: grafana/grafana:latest
    restart: unless-stopped
    ports:
      - "3001:3000"  # Mapped to 3001 to avoid conflict with frontend
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin}
      - GF_USERS_ALLOW_SIGN_UP=false
      - GF_INSTALL_PLUGINS=grafana-piechart-panel
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/local/provisioning:/etc/grafana/provisioning
      - ./grafana/local/dashboards:/var/lib/grafana/dashboards
    depends_on:
      - prometheus
    networks:
      - monitoring_network

  # Portainer for container management
  portainer:
    image: portainer/portainer-ce:latest
    restart: unless-stopped
    ports:
      - "9001:9000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
    networks:
      - monitoring_network

  # Adminer for database management
  adminer:
    image: adminer:latest
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      ADMINER_DEFAULT_SERVER: postgres
      ADMINER_DESIGN: hydra
    networks:
      - app_network

  # cAdvisor for container metrics
  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    restart: unless-stopped
    ports:
      - "8081:8080"
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    privileged: true
    devices:
      - /dev/kmsg
    networks:
      - monitoring_network

  # Node Exporter for system metrics
  node-exporter:
    image: prom/node-exporter:latest
    restart: unless-stopped
    ports:
      - "9100:9100"
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.rootfs=/rootfs'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    networks:
      - monitoring_network

networks:
  app_network:
    driver: bridge
  monitoring_network:
    driver: bridge

volumes:
  postgres_data:
  backend_logs:
  prometheus_data:
  grafana_data:
  portainer_data:

Local Deployment Script (deploy-local.sh)

Bash
#!/bin/bash

# Local Development Deployment Script for PRS
# This script manages the local development environment

set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Configuration
COMPOSE_FILE="compose.local.yml"
ENV_FILE="local/.env.local"

# Display help message
function show_help {
    echo -e "${BLUE}PRS Local Development Environment${NC}"
    echo "Usage: $0 [options]"
    echo ""
    echo "Options:"
    echo "  -h, --help                 Show this help message"
    echo "  -b, --build                Build all containers"
    echo "  -u, --up                   Start all containers"
    echo "  -d, --down                 Stop all containers"
    echo "  -r, --restart [service]    Restart all containers or specific service"
    echo "  -m, --migrate              Run database migrations"
    echo "  -s, --seed                 Run database seeders"
    echo "  -l, --logs [service]       Show logs for all services or specific service"
    echo "  -c, --clean                Clean up unused Docker resources"
    echo "  --init-db                  Initialize the database (migrate + seed)"
    echo "  --reset-db                 Reset the database (drop + recreate + migrate + seed)"
    echo "  --backup-db                Backup the database"
    echo "  --restore-db [file]        Restore the database from backup"
    echo "  --status                   Show status of all services"
    echo "  --setup                    Initial setup (create env files)"
    echo ""
    echo "Examples:"
    echo "  $0 --setup                 # Initial setup"
    echo "  $0 --build --up --init-db  # Full environment startup"
    echo "  $0 --restart backend       # Restart only backend service"
    echo "  $0 --logs frontend         # View frontend logs"
}

# Check if Docker is running
function check_docker {
    if ! docker info > /dev/null 2>&1; then
        echo -e "${RED}Error: Docker is not running or not installed${NC}"
        exit 1
    fi

    if ! docker compose version > /dev/null 2>&1; then
        echo -e "${RED}Error: docker compose is not installed or not available${NC}"
        exit 1
    fi
}

# Create local environment files
function setup_env {
    echo -e "${BLUE}Setting up local environment files...${NC}"

    # Create local directory
    mkdir -p local/{postgres,nginx,prometheus,grafana,loki}

    # Create .env.local if it doesn't exist
    if [[ ! -f "local/.env.local" ]]; then
        cat > "local/.env.local" << EOF
# Database Configuration
POSTGRES_DB=prs_local
POSTGRES_USER=admin
POSTGRES_PASSWORD=admin123



# API URLs
VITE_APP_API_URL=http://localhost/api

# Monitoring
GRAFANA_ADMIN_PASSWORD=admin
EOF
        echo -e "${GREEN}Created local/.env.local${NC}"
    fi

    # Create backend.local.env if it doesn't exist
    if [[ ! -f "local/backend.local.env" ]]; then
        cat > "local/backend.local.env" << EOF
# Application config
PORT=4000
HOST=0.0.0.0
JWT_SECRET=local-jwt-secret-key-for-development
NODE_ENV=development
OTP_KEY=U29tZVNlY3JldEtleVdpdGg2NEJ5dGVz
PASS_SECRET=12345
BYPASS_OTP=true
LOG_LEVEL=debug
DISABLE_REQUEST_LOGS=false

# Database configs
POSTGRES_SERVICE=postgres
POSTGRES_HOST=postgres
POSTGRES_DB=prs_local
POSTGRES_PORT=5432
POSTGRES_USER=admin
POSTGRES_PASSWORD=admin123
DIALECT=postgres
POOL_MIN=0
POOL_MAX=5
DISABLE_SSL=true



# Root User
ROOT_USER_NAME=admin
ROOT_USER_EMAIL=admin@local.dev
ROOT_USER_PASSWORD=admin123

# PLG Stack Integration
PROMETHEUS_ENABLED=true

# Development Features
ENABLE_DEBUG_ROUTES=true
ENABLE_SWAGGER=true
CORS_ORIGIN=http://localhost:3000,http://localhost
EOF
        echo -e "${GREEN}Created local/backend.local.env${NC}"
    fi

    # Create frontend.local.env if it doesn't exist
    if [[ ! -f "local/frontend.local.env" ]]; then
        cat > "local/frontend.local.env" << EOF
# API Configuration
VITE_APP_API_URL=http://localhost/api
VITE_APP_UPLOAD_URL=http://localhost/api/upload

# Development Features
VITE_APP_ENABLE_API_MOCKING=false
VITE_APP_ENABLE_DEVTOOLS=true

# PLG Stack Integration
VITE_APP_PROMETHEUS_GATEWAY=http://localhost:9091
VITE_APP_GRAFANA_URL=http://localhost:3001

# Logging Configuration
VITE_APP_LOG_LEVEL=debug
VITE_APP_LOG_BATCH_SIZE=10
VITE_APP_LOG_FLUSH_INTERVAL=5000

# Application Metadata
VITE_APP_APP_VERSION=1.0.0-local
VITE_APP_BUILD_ID=local-development
VITE_APP_ENVIRONMENT=local
EOF
        echo -e "${GREEN}Created local/frontend.local.env${NC}"
    fi

    echo -e "${GREEN}Environment setup complete!${NC}"
}

# Parse command line arguments
BUILD=false
UP=false
DOWN=false
RESTART=false
RESTART_SERVICE=""
MIGRATE=false
SEED=false
LOGS=false
LOGS_SERVICE=""
CLEAN=false
INIT_DB=false
RESET_DB=false
BACKUP_DB=false
RESTORE_DB=false
RESTORE_FILE=""
STATUS=false
SETUP=false

while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--help)
            show_help
            exit 0
            ;;
        -b|--build)
            BUILD=true
            shift
            ;;
        -u|--up)
            UP=true
            shift
            ;;
        -d|--down)
            DOWN=true
            shift
            ;;
        -r|--restart)
            RESTART=true
            if [[ $2 && $2 != -* ]]; then
                RESTART_SERVICE=$2
                shift
            fi
            shift
            ;;
        -m|--migrate)
            MIGRATE=true
            shift
            ;;
        -s|--seed)
            SEED=true
            shift
            ;;
        -l|--logs)
            LOGS=true
            if [[ $2 && $2 != -* ]]; then
                LOGS_SERVICE=$2
                shift
            fi
            shift
            ;;
        -c|--clean)
            CLEAN=true
            shift
            ;;
        --init-db)
            INIT_DB=true
            shift
            ;;
        --reset-db)
            RESET_DB=true
            shift
            ;;
        --backup-db)
            BACKUP_DB=true
            shift
            ;;
        --restore-db)
            RESTORE_DB=true
            if [[ $2 && $2 != -* ]]; then
                RESTORE_FILE=$2
                shift
            fi
            shift
            ;;
        --status)
            STATUS=true
            shift
            ;;
        --setup)
            SETUP=true
            shift
            ;;
        *)
            echo -e "${RED}Unknown option: $1${NC}"
            show_help
            exit 1
            ;;
    esac
done

# Check Docker
check_docker

# If no options provided, show help
if [[ $BUILD == false && $UP == false && $DOWN == false && $RESTART == false && $MIGRATE == false && $SEED == false && $LOGS == false && $CLEAN == false && $INIT_DB == false && $RESET_DB == false && $BACKUP_DB == false && $RESTORE_DB == false && $STATUS == false && $SETUP == false ]]; then
    show_help
    exit 0
fi

# Setup environment
if [[ $SETUP == true ]]; then
    setup_env
fi

# Build containers
if [[ $BUILD == true ]]; then
    echo -e "${BLUE}Building containers...${NC}"
    docker compose -f $COMPOSE_FILE --env-file $ENV_FILE build
fi

# Start containers
if [[ $UP == true ]]; then
    echo -e "${BLUE}Starting containers...${NC}"
    docker compose -f $COMPOSE_FILE --env-file $ENV_FILE up -d
    echo -e "${GREEN}Containers started successfully!${NC}"
    echo ""
    echo -e "${BLUE}Access URLs:${NC}"
    echo "  PRS Application: http://localhost"
    echo "  Frontend Dev:    http://localhost:3000"
    echo "  Backend API:     http://localhost:4000"
    echo "  Grafana:         http://localhost:3001 (admin/admin)"
    echo "  Prometheus:      http://localhost:9090"
    echo "  Portainer:       http://localhost:9001"
    echo "  MinIO Console:   http://localhost:9002"
    echo "  Adminer:         http://localhost:8080"
fi

# Stop containers
if [[ $DOWN == true ]]; then
    echo -e "${BLUE}Stopping containers...${NC}"
    docker compose -f $COMPOSE_FILE --env-file $ENV_FILE down
fi

# Restart containers
if [[ $RESTART == true ]]; then
    if [[ -n $RESTART_SERVICE ]]; then
        echo -e "${BLUE}Restarting $RESTART_SERVICE...${NC}"
        docker compose -f $COMPOSE_FILE --env-file $ENV_FILE restart $RESTART_SERVICE
    else
        echo -e "${BLUE}Restarting all containers...${NC}"
        docker compose -f $COMPOSE_FILE --env-file $ENV_FILE restart
    fi
fi

# Run migrations
if [[ $MIGRATE == true ]]; then
    echo -e "${BLUE}Running migrations...${NC}"
    docker compose -f $COMPOSE_FILE --env-file $ENV_FILE exec backend npm run migrate
fi

# Run seeders
if [[ $SEED == true ]]; then
    echo -e "${BLUE}Running seeders...${NC}"
    docker compose -f $COMPOSE_FILE --env-file $ENV_FILE exec backend npm run seed
fi

# Initialize database
if [[ $INIT_DB == true ]]; then
    echo -e "${BLUE}Initializing database...${NC}"
    docker compose -f $COMPOSE_FILE --env-file $ENV_FILE exec backend npm run migrate
    docker compose -f $COMPOSE_FILE --env-file $ENV_FILE exec backend npm run seed
fi

# Reset database
if [[ $RESET_DB == true ]]; then
    echo -e "${YELLOW}Resetting database...${NC}"
    docker compose -f $COMPOSE_FILE --env-file $ENV_FILE down
    docker volume rm deployment_postgres_data 2>/dev/null || true
    docker compose -f $COMPOSE_FILE --env-file $ENV_FILE up -d postgres
    sleep 5
    docker compose -f $COMPOSE_FILE --env-file $ENV_FILE up -d backend
    sleep 10
    docker compose -f $COMPOSE_FILE --env-file $ENV_FILE exec backend npm run migrate
    docker compose -f $COMPOSE_FILE --env-file $ENV_FILE exec backend npm run seed
fi

# Show logs
if [[ $LOGS == true ]]; then
    if [[ -n $LOGS_SERVICE ]]; then
        echo -e "${BLUE}Showing logs for $LOGS_SERVICE...${NC}"
        docker compose -f $COMPOSE_FILE --env-file $ENV_FILE logs -f $LOGS_SERVICE
    else
        echo -e "${BLUE}Showing logs for all services...${NC}"
        docker compose -f $COMPOSE_FILE --env-file $ENV_FILE logs -f
    fi
fi

# Show status
if [[ $STATUS == true ]]; then
    echo -e "${BLUE}Service Status:${NC}"
    docker compose -f $COMPOSE_FILE --env-file $ENV_FILE ps
fi

# Clean up
if [[ $CLEAN == true ]]; then
    echo -e "${BLUE}Cleaning up Docker resources...${NC}"
    docker system prune -f
fi

# Backup database
if [[ $BACKUP_DB == true ]]; then
    TIMESTAMP=$(date +%Y%m%d_%H%M%S)
    BACKUP_FILE="prs_local_backup_$TIMESTAMP.sql"
    echo -e "${BLUE}Backing up database to $BACKUP_FILE...${NC}"
    docker compose -f $COMPOSE_FILE --env-file $ENV_FILE exec -T postgres pg_dump -U admin prs_local > $BACKUP_FILE
    echo -e "${GREEN}Backup completed: $BACKUP_FILE${NC}"
fi

# Restore database
if [[ $RESTORE_DB == true ]]; then
    if [[ ! -f $RESTORE_FILE ]]; then
        echo -e "${RED}Error: Backup file $RESTORE_FILE not found${NC}"
        exit 1
    fi
    echo -e "${BLUE}Restoring database from $RESTORE_FILE...${NC}"
    cat $RESTORE_FILE | docker compose -f $COMPOSE_FILE --env-file $ENV_FILE exec -T postgres psql -U admin prs_local
    echo -e "${GREEN}Restore completed${NC}"
fi

echo -e "${GREEN}Done!${NC}"

Nginx Configuration (nginx/local/default.conf)

Nginx Configuration File
# Local development Nginx configuration
upstream backend_servers {
    server backend:4000;
}

upstream frontend_servers {
    server frontend:3000;
}

# Main server block
server {
    listen 80;
    server_name localhost;

    # Increase client max body size for file uploads
    client_max_body_size 100M;

    # Frontend (React app)
    location / {
        proxy_pass http://frontend_servers;
        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;

        # WebSocket support for HMR
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # CORS headers for development
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
        add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization";
    }

    # Backend API
    location /api/ {
        proxy_pass http://backend_servers/;
        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;

        # CORS headers for development
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
        add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization";

        # Handle preflight requests
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin *;
            add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
            add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization";
            add_header Content-Length 0;
            add_header Content-Type text/plain;
            return 204;
        }
    }

    # Static file uploads
    location /uploads/ {
        alias /usr/share/nginx/html/upload/;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

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