# version is obsolete in newer docker-compose versions # # Environment-aware Docker Compose configuration # Supports both production (fellowship) and local dev (fellowship-local) stacks # Usage: # Production: cp .env.prod .env && docker-compose up -d (containers: fellowship_*) # Local dev: cp .env.local .env && docker-compose up -d (containers: fellowship-local_*) # # The COMPOSE_PROJECT_NAME environment variable controls container naming: # - Omitted or 'fellowship' → containers: fellowship_backend_1, fellowship_frontend_1 # - 'fellowship-local' → containers: fellowship-local_backend_1, fellowship-local_frontend_1 # # This allows both environments to coexist without conflicts. services: backend: build: context: ./sut/backend dockerfile: Dockerfile # ✓ No container_name: allows COMPOSE_PROJECT_NAME-based naming # Port 5000 is intentionally NOT exposed to the host — backend is only reached # via Caddy reverse-proxy (internal Docker network: backend:5000). # macOS AirPlay Receiver occupies host port 5000 (Monterey+), so binding it # would fail. Direct API access for debugging: docker exec or /api through Caddy. volumes: - backend_data:/app/data - ./sut/backend:/app # Exclude node_modules and data from volume mount to avoid conflicts environment: - FLASK_APP=app.py - FLASK_ENV=development - DATABASE_URL=sqlite:////app/data/fellowship.db - SECRET_KEY=dev-secret-key-change-in-production - AZURE_OPENAI_ENDPOINT=${AZURE_OPENAI_ENDPOINT:-} - AZURE_OPENAI_API_KEY=${AZURE_OPENAI_API_KEY:-} - AZURE_OPENAI_DEPLOYMENT=${AZURE_OPENAI_DEPLOYMENT:-} - AZURE_OPENAI_API_VERSION=${AZURE_OPENAI_API_VERSION:-} working_dir: /app command: python app.py restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5000/api/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s frontend: build: context: ./sut/frontend dockerfile: Dockerfile # ✓ No container_name: allows COMPOSE_PROJECT_NAME-based naming # Port 3000 is intentionally NOT exposed to the host — Caddy reverse-proxies # to frontend:3000 via the internal Docker network. Colima's sshfs SSH tunnels # conflict with host-bound :3000, so we leave it unmapped. volumes: - ./sut/frontend:/app - frontend_node_modules:/app/node_modules environment: - REACT_APP_API_URL=/api - REACT_APP_ENABLE_TEST_CONTROLS=${REACT_APP_ENABLE_TEST_CONTROLS:-true} - CHOKIDAR_USEPOLLING=true - SKIP_PREFLIGHT_CHECK=true - DISABLE_ESLINT_PLUGIN=true - FAST_REFRESH=false - FRONTEND_MODE=${FRONTEND_MODE:-dev} - NODE_ENV=development - WDS_SOCKET_PORT=${WDS_SOCKET_PORT:-80} - WDS_SOCKET_HOST=${CADDY_DOMAIN:-localhost} - WDS_SOCKET_PROTOCOL=${WDS_SOCKET_PROTOCOL:-ws} - WDS_SOCKET_PATH=${WDS_SOCKET_PATH:-/ws} command: sh -c "npm install && if [ \"${FRONTEND_MODE:-dev}\" = \"prod\" ]; then npm run build && npx --yes serve -s build -l 3000; else npm start; fi" depends_on: - backend restart: unless-stopped caddy: image: caddy:2-alpine # ✓ No container_name: allows COMPOSE_PROJECT_NAME-based naming ports: - "80:80" - "443:443" volumes: - ${CADDYFILE_PATH:-./caddy/Caddyfile.local}:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config environment: # Use CADDY_DOMAIN from environment/env file, fallback to localhost for local development CADDY_DOMAIN: ${CADDY_DOMAIN:-localhost} # DevOps Escape Room HTTPS subdomains — prepend jenkins-/ide- to CADDY_DOMAIN. # These env vars are required when using Caddyfile (staging) or Caddyfile.prod. # They are NOT needed when using Caddyfile.local (CI / local HTTP-only dev). JENKINS_DOMAIN: ${JENKINS_DOMAIN:-} IDE_DOMAIN: ${IDE_DOMAIN:-} GITEA_DOMAIN: ${GITEA_DOMAIN:-} # Allow Caddy to reach services in the devops-escape-room compose stack # (Jenkins on host:8080 and code-server on host:8443) via host.docker.internal. extra_hosts: - "host.docker.internal:host-gateway" depends_on: - backend - frontend restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost/"] interval: 10s timeout: 5s retries: 3 start_period: 20s volumes: backend_data: driver: local frontend_node_modules: driver: local caddy_data: driver: local caddy_config: driver: local