# compose.dev.yml # ============================================================================ # DEVELOPMENT DOCKER COMPOSE CONFIGURATION # ============================================================================ # This file defines the local development environment using Docker/Podman. # # Services: # - app: Node.js application (API + Frontend + Bugsink + Logstash) # - postgres: PostgreSQL 15 with PostGIS extension # - redis: Redis for caching and job queues # # Usage: # Start all services: podman-compose -f compose.dev.yml up -d # Stop all services: podman-compose -f compose.dev.yml down # View logs: podman-compose -f compose.dev.yml logs -f # Reset everything: podman-compose -f compose.dev.yml down -v # # VS Code Dev Containers: # This file is referenced by .devcontainer/devcontainer.json for seamless # VS Code integration. Open the project in VS Code and use "Reopen in Container". # # Bugsink (ADR-015): # Access error tracking UI at http://localhost:8000 # Default login: admin@localhost / admin # ============================================================================ version: '3.8' services: # =================== # Application Service # =================== app: container_name: flyer-crawler-dev # Use pre-built image if available, otherwise build from Dockerfile.dev # To build: podman build -f Dockerfile.dev -t flyer-crawler-dev:latest . image: localhost/flyer-crawler-dev:latest build: context: . dockerfile: Dockerfile.dev volumes: # Mount the current directory to /app in the container - .:/app # Create a volume for node_modules to avoid conflicts with Windows host # and improve performance. - node_modules_data:/app/node_modules # Mount PostgreSQL logs for Logstash access (ADR-050) - postgres_logs:/var/log/postgresql:ro # Mount Redis logs for Logstash access (ADR-050) - redis_logs:/var/log/redis:ro # Mount PM2 logs for Logstash access (ADR-050, ADR-014) - pm2_logs:/var/log/pm2 ports: - '80:80' # HTTP redirect to HTTPS (matches production) - '443:443' # Frontend HTTPS (nginx proxies Vite 5173 → 443) - '3001:3001' # Backend API - '8000:8000' # Bugsink error tracking HTTP (ADR-015) - '8443:8443' # Bugsink error tracking HTTPS (ADR-015) environment: # Timezone: PST (America/Los_Angeles) for consistent log timestamps - TZ=America/Los_Angeles # Core settings - NODE_ENV=development # Database - use service name for Docker networking - DB_HOST=postgres - DB_PORT=5432 - DB_USER=postgres - DB_PASSWORD=postgres - DB_NAME=flyer_crawler_dev # Redis - use service name for Docker networking - REDIS_URL=redis://redis:6379 - REDIS_HOST=redis - REDIS_PORT=6379 # Frontend URL for CORS - FRONTEND_URL=http://localhost:3000 # Default JWT secret for development (override in production!) - JWT_SECRET=dev-jwt-secret-change-in-production # Worker settings - WORKER_LOCK_DURATION=120000 # Bugsink error tracking (ADR-015) - BUGSINK_DB_HOST=postgres - BUGSINK_DB_PORT=5432 - BUGSINK_DB_NAME=bugsink - BUGSINK_DB_USER=bugsink - BUGSINK_DB_PASSWORD=bugsink_dev_password - BUGSINK_PORT=8000 - BUGSINK_BASE_URL=https://localhost:8443 - BUGSINK_ADMIN_EMAIL=admin@localhost - BUGSINK_ADMIN_PASSWORD=admin - BUGSINK_SECRET_KEY=dev-bugsink-secret-key-minimum-50-characters-for-security # Sentry SDK configuration (points to local Bugsink HTTP) # Note: Using HTTP with 127.0.0.1 instead of localhost because Sentry SDK # doesn't accept 'localhost' as a valid hostname in DSN validation # The browser accesses Bugsink at http://localhost:8000 (nginx proxies to HTTPS for the app) - SENTRY_DSN=http://cea01396-c562-46ad-b587-8fa5ee6b1d22@127.0.0.1:8000/1 - VITE_SENTRY_DSN=http://d92663cb-73cf-4145-b677-b84029e4b762@127.0.0.1:8000/2 - SENTRY_ENVIRONMENT=development - VITE_SENTRY_ENVIRONMENT=development - SENTRY_ENABLED=true - VITE_SENTRY_ENABLED=true - SENTRY_DEBUG=true - VITE_SENTRY_DEBUG=true depends_on: postgres: condition: service_healthy redis: condition: service_healthy # Start dev server automatically (works with or without VS Code) command: /app/scripts/dev-entrypoint.sh # Healthcheck for the app (once it's running) healthcheck: test: ['CMD', 'curl', '-f', 'http://localhost:3001/api/health/live'] interval: 30s timeout: 10s retries: 3 start_period: 60s # =================== # PostgreSQL Database # =================== postgres: image: docker.io/postgis/postgis:15-3.4 container_name: flyer-crawler-postgres ports: - '5432:5432' environment: # Timezone: PST (America/Los_Angeles) for consistent log timestamps TZ: America/Los_Angeles # PostgreSQL timezone setting (used by log_timezone and timezone parameters) PGTZ: America/Los_Angeles POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: flyer_crawler_dev # Optimize for development POSTGRES_INITDB_ARGS: '--encoding=UTF8 --locale=C' volumes: - postgres_data:/var/lib/postgresql/data # Mount init scripts to run on first database creation # Scripts run in alphabetical order: 00-extensions, 01-bugsink - ./sql/00-init-extensions.sql:/docker-entrypoint-initdb.d/00-init-extensions.sql:ro - ./sql/01-init-bugsink.sh:/docker-entrypoint-initdb.d/01-init-bugsink.sh:ro # Mount custom PostgreSQL configuration (ADR-050) - ./docker/postgres/postgresql.conf.override:/etc/postgresql/postgresql.conf.d/custom.conf:ro # Create log volume for Logstash access (ADR-050) - postgres_logs:/var/log/postgresql # Override postgres command to include custom config (ADR-050) command: > postgres -c config_file=/var/lib/postgresql/data/postgresql.conf -c hba_file=/var/lib/postgresql/data/pg_hba.conf -c timezone=America/Los_Angeles -c log_timezone=America/Los_Angeles -c log_min_messages=notice -c client_min_messages=notice -c logging_collector=on -c log_destination=stderr -c log_directory=/var/log/postgresql -c log_filename=postgresql-%Y-%m-%d.log -c log_rotation_age=1d -c log_rotation_size=100MB -c log_truncate_on_rotation=on -c log_line_prefix='%t [%p] %u@%d ' -c log_min_duration_statement=1000 -c log_statement=none -c log_connections=on -c log_disconnections=on # Healthcheck ensures postgres is ready before app starts healthcheck: test: ['CMD-SHELL', 'pg_isready -U postgres -d flyer_crawler_dev'] interval: 5s timeout: 5s retries: 10 start_period: 10s # =================== # Redis Cache/Queue # =================== redis: image: docker.io/library/redis:alpine container_name: flyer-crawler-redis # Run as root to allow entrypoint to set up log directory permissions # Redis will drop privileges after setup via gosu in entrypoint user: root ports: - '6379:6379' environment: # Timezone: PST (America/Los_Angeles) for consistent log timestamps TZ: America/Los_Angeles volumes: - redis_data:/data # Create log volume for Logstash access (ADR-050) - redis_logs:/var/log/redis # Mount custom entrypoint for log directory setup (ADR-050) - ./docker/redis/docker-entrypoint.sh:/usr/local/bin/docker-entrypoint.sh:ro # Healthcheck ensures redis is ready before app starts healthcheck: test: ['CMD', 'redis-cli', 'ping'] interval: 5s timeout: 5s retries: 10 start_period: 5s # Enable persistence and file logging for Logstash (ADR-050) # Redis log levels: debug, verbose, notice (default), warning # Custom entrypoint sets up log directory with correct permissions entrypoint: ['/usr/local/bin/docker-entrypoint.sh'] command: - --appendonly - 'yes' - --logfile - /var/log/redis/redis-server.log - --loglevel - notice # =================== # Named Volumes # =================== volumes: postgres_data: name: flyer-crawler-postgres-data postgres_logs: name: flyer-crawler-postgres-logs redis_data: name: flyer-crawler-redis-data redis_logs: name: flyer-crawler-redis-logs pm2_logs: name: flyer-crawler-pm2-logs node_modules_data: name: flyer-crawler-node-modules # =================== # Network Configuration # =================== # All services are on the default bridge network. # Use service names (postgres, redis) as hostnames.