# ================================================================ # OPTIMIZED DOCKER COMPOSE - NO ENVIRONMENT DUPLICATION # Single source of truth: .env file only # ================================================================ # ================================================================ # NETWORKS & VOLUMES (same as before) # ================================================================ networks: bakery-network: driver: bridge ipam: config: - subnet: 172.20.0.0/16 volumes: auth_db_data: training_db_data: forecasting_db_data: data_db_data: tenant_db_data: notification_db_data: redis_data: rabbitmq_data: prometheus_data: grafana_data: model_storage: log_storage: # ================================================================ # SERVICES - USING ONLY .env FILE # ================================================================ services: # ================================================================ # INFRASTRUCTURE - NO DUPLICATION # ================================================================ redis: image: redis:7-alpine container_name: bakery-redis restart: unless-stopped # ONLY use environment substitution from .env command: > redis-server --appendonly yes --requirepass ${REDIS_PASSWORD} --maxmemory ${REDIS_MAX_MEMORY:-512mb} --databases 16 ports: - "${REDIS_PORT}:6379" volumes: - redis_data:/data networks: bakery-network: ipv4_address: 172.20.0.10 healthcheck: test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] interval: 30s timeout: 10s retries: 3 rabbitmq: image: rabbitmq:3.12-management-alpine container_name: bakery-rabbitmq restart: unless-stopped # ONLY use environment substitution from .env environment: - RABBITMQ_DEFAULT_USER=${RABBITMQ_USER} - RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASSWORD} - RABBITMQ_DEFAULT_VHOST=${RABBITMQ_VHOST} ports: - "${RABBITMQ_PORT}:5672" - "${RABBITMQ_MANAGEMENT_PORT}:15672" volumes: - rabbitmq_data:/var/lib/rabbitmq networks: bakery-network: ipv4_address: 172.20.0.11 healthcheck: test: ["CMD", "rabbitmq-diagnostics", "ping"] interval: 30s timeout: 10s retries: 3 # ================================================================ # DATABASES - NO DUPLICATION # ================================================================ auth-db: image: postgres:15-alpine container_name: bakery-auth-db restart: unless-stopped # ONLY reference .env variables environment: - POSTGRES_DB=${AUTH_DB_NAME} - POSTGRES_USER=${AUTH_DB_USER} - POSTGRES_PASSWORD=${AUTH_DB_PASSWORD} - POSTGRES_INITDB_ARGS=${POSTGRES_INITDB_ARGS} - PGDATA=/var/lib/postgresql/data/pgdata volumes: - auth_db_data:/var/lib/postgresql/data networks: bakery-network: ipv4_address: 172.20.0.20 healthcheck: test: ["CMD-SHELL", "pg_isready -U ${AUTH_DB_USER} -d ${AUTH_DB_NAME}"] interval: 10s timeout: 5s retries: 5 training-db: image: postgres:15-alpine container_name: bakery-training-db restart: unless-stopped environment: - POSTGRES_DB=${TRAINING_DB_NAME} - POSTGRES_USER=${TRAINING_DB_USER} - POSTGRES_PASSWORD=${TRAINING_DB_PASSWORD} - POSTGRES_INITDB_ARGS=${POSTGRES_INITDB_ARGS} - PGDATA=/var/lib/postgresql/data/pgdata volumes: - training_db_data:/var/lib/postgresql/data networks: bakery-network: ipv4_address: 172.20.0.21 healthcheck: test: ["CMD-SHELL", "pg_isready -U ${TRAINING_DB_USER} -d ${TRAINING_DB_NAME}"] interval: 10s timeout: 5s retries: 5 forecasting-db: image: postgres:15-alpine container_name: bakery-forecasting-db restart: unless-stopped environment: - POSTGRES_DB=${FORECASTING_DB_NAME} - POSTGRES_USER=${FORECASTING_DB_USER} - POSTGRES_PASSWORD=${FORECASTING_DB_PASSWORD} - POSTGRES_INITDB_ARGS=${POSTGRES_INITDB_ARGS} - PGDATA=/var/lib/postgresql/data/pgdata volumes: - forecasting_db_data:/var/lib/postgresql/data networks: bakery-network: ipv4_address: 172.20.0.22 healthcheck: test: ["CMD-SHELL", "pg_isready -U ${FORECASTING_DB_USER} -d ${FORECASTING_DB_NAME}"] interval: 10s timeout: 5s retries: 5 data-db: image: postgres:15-alpine container_name: bakery-data-db restart: unless-stopped environment: - POSTGRES_DB=${DATA_DB_NAME} - POSTGRES_USER=${DATA_DB_USER} - POSTGRES_PASSWORD=${DATA_DB_PASSWORD} - POSTGRES_INITDB_ARGS=${POSTGRES_INITDB_ARGS} - PGDATA=/var/lib/postgresql/data/pgdata volumes: - data_db_data:/var/lib/postgresql/data networks: bakery-network: ipv4_address: 172.20.0.23 healthcheck: test: ["CMD-SHELL", "pg_isready -U ${DATA_DB_USER} -d ${DATA_DB_NAME}"] interval: 10s timeout: 5s retries: 5 tenant-db: image: postgres:15-alpine container_name: bakery-tenant-db restart: unless-stopped environment: - POSTGRES_DB=${TENANT_DB_NAME} - POSTGRES_USER=${TENANT_DB_USER} - POSTGRES_PASSWORD=${TENANT_DB_PASSWORD} - POSTGRES_INITDB_ARGS=${POSTGRES_INITDB_ARGS} - PGDATA=/var/lib/postgresql/data/pgdata volumes: - tenant_db_data:/var/lib/postgresql/data networks: bakery-network: ipv4_address: 172.20.0.24 healthcheck: test: ["CMD-SHELL", "pg_isready -U ${TENANT_DB_USER} -d ${TENANT_DB_NAME}"] interval: 10s timeout: 5s retries: 5 notification-db: image: postgres:15-alpine container_name: bakery-notification-db restart: unless-stopped environment: - POSTGRES_DB=${NOTIFICATION_DB_NAME} - POSTGRES_USER=${NOTIFICATION_DB_USER} - POSTGRES_PASSWORD=${NOTIFICATION_DB_PASSWORD} - POSTGRES_INITDB_ARGS=${POSTGRES_INITDB_ARGS} - PGDATA=/var/lib/postgresql/data/pgdata volumes: - notification_db_data:/var/lib/postgresql/data networks: bakery-network: ipv4_address: 172.20.0.25 healthcheck: test: ["CMD-SHELL", "pg_isready -U ${NOTIFICATION_DB_USER} -d ${NOTIFICATION_DB_NAME}"] interval: 10s timeout: 5s retries: 5 # ================================================================ # LOCATION SERVICES (NEW SECTION) # ================================================================ nominatim-db: image: postgis/postgis:15-3.3 # Use PostGIS enabled PostgreSQL image container_name: bakery-nominatim-db restart: unless-stopped environment: - POSTGRES_DB=${NOMINATIM_DB_NAME} - POSTGRES_USER=${NOMINATIM_DB_USER} - POSTGRES_PASSWORD=${NOMINATIM_DB_PASSWORD} - PGDATA=/var/lib/postgresql/data/pgdata - POSTGRES_INITDB_ARGS="--auth-host=scram-sha-256" # Recommended for PostGIS volumes: - nominatim_db_data:/var/lib/postgresql/data profiles: - development networks: bakery-network: ipv4_address: 172.20.0.30 # Assign a static IP for Nominatim to find it healthcheck: test: ["CMD-SHELL", "pg_isready -U ${NOMINATIM_DB_USER} -d ${NOMINATIM_DB_NAME}"] interval: 10s timeout: 5s retries: 5 nominatim: image: mediagis/nominatim:4.2 # A pre-built Nominatim image container_name: bakery-nominatim restart: unless-stopped env_file: .env # Load environment variables from .env file environment: # Database connection details for Nominatim - POSTGRES_HOST=nominatim-db - POSTGRES_PORT=5432 - POSTGRES_USER=${NOMINATIM_DB_USER} - POSTGRES_PASSWORD=${NOMINATIM_DB_PASSWORD} - POSTGRES_DB=${NOMINATIM_DB_NAME} - PBF_URL=${NOMINATIM_PBF_URL} # URL to your OpenStreetMap PBF data (e.g., Spain) ports: - "${NOMINATIM_PORT}:8080" # Expose Nominatim web interface volumes: - nominatim_data:/var/lib/nominatim # Persistent storage for Nominatim data and configuration networks: bakery-network: ipv4_address: 172.20.0.120 # Assign a static IP for Nominatim service depends_on: nominatim-db: condition: service_healthy # Ensure database is ready before Nominatim starts # By default, mediagis/nominatim image will try to import data on first run # if PBF_URL is set and the database is empty. profiles: - development healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/nominatim/status.php"] interval: 30s timeout: 10s retries: 3 deploy: resources: limits: memory: ${NOMINATIM_MEMORY_LIMIT:-8G} # Nominatim is memory-intensive for import cpus: '${NOMINATIM_CPU_LIMIT:-4}' # Adjust based on your system and data # ================================================================ # MICROSERVICES - CLEAN APPROACH # ================================================================ gateway: build: context: . dockerfile: ./gateway/Dockerfile args: - ENVIRONMENT=${ENVIRONMENT} - BUILD_DATE=${BUILD_DATE} image: bakery/gateway:${IMAGE_TAG} container_name: bakery-gateway restart: unless-stopped # ONLY load from .env file - no duplication env_file: .env ports: - "${GATEWAY_PORT}:8000" depends_on: redis: condition: service_healthy rabbitmq: condition: service_healthy networks: bakery-network: ipv4_address: 172.20.0.100 volumes: - log_storage:/app/logs - ./gateway:/app - ./shared:/app/shared healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 auth-service: build: context: . dockerfile: ./services/auth/Dockerfile args: - ENVIRONMENT=${ENVIRONMENT} - BUILD_DATE=${BUILD_DATE} image: bakery/auth-service:${IMAGE_TAG} container_name: bakery-auth-service restart: unless-stopped # ONLY load from .env file - no duplication env_file: .env ports: - "${AUTH_SERVICE_PORT}:8000" depends_on: auth-db: condition: service_healthy redis: condition: service_healthy rabbitmq: condition: service_healthy networks: bakery-network: ipv4_address: 172.20.0.101 volumes: - log_storage:/app/logs - ./services/auth:/app - ./shared:/app/shared healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 tenant-service: build: context: . dockerfile: ./services/tenant/Dockerfile args: - ENVIRONMENT=${ENVIRONMENT} - BUILD_DATE=${BUILD_DATE} image: bakery/tenant-service:${IMAGE_TAG} container_name: bakery-tenant-service restart: unless-stopped env_file: .env ports: - "${TENANT_SERVICE_PORT}:8000" depends_on: tenant-db: condition: service_healthy redis: condition: service_healthy rabbitmq: condition: service_healthy auth-service: condition: service_healthy networks: bakery-network: ipv4_address: 172.20.0.105 volumes: - log_storage:/app/logs - ./services/tenant:/app - ./shared:/app/shared healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 training-service: build: context: . dockerfile: ./services/training/Dockerfile args: - ENVIRONMENT=${ENVIRONMENT} - BUILD_DATE=${BUILD_DATE} image: bakery/training-service:${IMAGE_TAG} container_name: bakery-training-service restart: unless-stopped env_file: .env ports: - "${TRAINING_SERVICE_PORT}:8000" depends_on: training-db: condition: service_healthy redis: condition: service_healthy rabbitmq: condition: service_healthy auth-service: condition: service_healthy data-service: condition: service_healthy networks: bakery-network: ipv4_address: 172.20.0.102 volumes: - log_storage:/app/logs - model_storage:/app/models - ./services/training:/app - ./shared:/app/shared healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 deploy: resources: limits: memory: ${TRAINING_MEMORY_LIMIT:-2G} cpus: '${TRAINING_CPU_LIMIT:-1.5}' forecasting-service: build: context: . dockerfile: ./services/forecasting/Dockerfile args: - ENVIRONMENT=${ENVIRONMENT} - BUILD_DATE=${BUILD_DATE} image: bakery/forecasting-service:${IMAGE_TAG} container_name: bakery-forecasting-service restart: unless-stopped env_file: .env ports: - "${FORECASTING_SERVICE_PORT}:8000" depends_on: forecasting-db: condition: service_healthy redis: condition: service_healthy rabbitmq: condition: service_healthy auth-service: condition: service_healthy training-service: condition: service_healthy networks: bakery-network: ipv4_address: 172.20.0.103 volumes: - log_storage:/app/logs - model_storage:/app/models - ./services/forecasting:/app - ./shared:/app/shared healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 data-service: build: context: . dockerfile: ./services/data/Dockerfile args: - ENVIRONMENT=${ENVIRONMENT} - BUILD_DATE=${BUILD_DATE} image: bakery/data-service:${IMAGE_TAG} container_name: bakery-data-service restart: unless-stopped env_file: .env ports: - "${DATA_SERVICE_PORT}:8000" depends_on: data-db: condition: service_healthy redis: condition: service_healthy rabbitmq: condition: service_healthy auth-service: condition: service_healthy networks: bakery-network: ipv4_address: 172.20.0.104 volumes: - log_storage:/app/logs - ./services/data:/app - ./shared:/app/shared healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 notification-service: build: context: . dockerfile: ./services/notification/Dockerfile args: - ENVIRONMENT=${ENVIRONMENT} - BUILD_DATE=${BUILD_DATE} image: bakery/notification-service:${IMAGE_TAG} container_name: bakery-notification-service restart: unless-stopped env_file: .env ports: - "${NOTIFICATION_SERVICE_PORT}:8000" depends_on: notification-db: condition: service_healthy redis: condition: service_healthy rabbitmq: condition: service_healthy auth-service: condition: service_healthy networks: bakery-network: ipv4_address: 172.20.0.106 volumes: - log_storage:/app/logs - ./services/notification:/app - ./shared:/app/shared healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 # ================================================================ # MONITORING - SIMPLE APPROACH # ================================================================ prometheus: image: prom/prometheus:v2.45.0 container_name: bakery-prometheus restart: unless-stopped command: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.path=/prometheus' - '--storage.tsdb.retention.time=${PROMETHEUS_RETENTION:-200h}' - '--web.enable-lifecycle' ports: - "${PROMETHEUS_PORT}:9090" volumes: - ./infrastructure/monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus_data:/prometheus networks: bakery-network: ipv4_address: 172.20.0.200 grafana: image: grafana/grafana:10.0.0 container_name: bakery-grafana restart: unless-stopped environment: - GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER} - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD} - GF_USERS_ALLOW_SIGN_UP=false - GF_DEFAULT_TIMEZONE=${TIMEZONE} - GF_SERVER_ROOT_URL=${GRAFANA_ROOT_URL} ports: - "${GRAFANA_PORT}:3000" volumes: - grafana_data:/var/lib/grafana - ./infrastructure/monitoring/grafana:/etc/grafana/provisioning:ro networks: bakery-network: ipv4_address: 172.20.0.201 depends_on: - prometheus # ================================================================ # FRONTEND - CLEAN CONFIG # ================================================================ dashboard: build: context: ./frontend dockerfile: Dockerfile.${ENVIRONMENT} args: - NEXT_PUBLIC_API_URL=${FRONTEND_API_URL} - NEXT_PUBLIC_WS_URL=${FRONTEND_WS_URL} - NEXT_PUBLIC_ENVIRONMENT=${ENVIRONMENT} image: bakery/dashboard:${IMAGE_TAG} container_name: bakery-dashboard restart: unless-stopped ports: - "${DASHBOARD_PORT}:3000" depends_on: gateway: condition: service_healthy networks: bakery-network: ipv4_address: 172.20.0.110 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/"] interval: 30s timeout: 10s retries: 3 # ================================================================ # DEVELOPMENT TOOLS - OPTIONAL # ================================================================ pgadmin: image: dpage/pgadmin4:7.4 container_name: bakery-pgadmin restart: unless-stopped environment: - PGADMIN_DEFAULT_EMAIL=${PGADMIN_EMAIL} - PGADMIN_DEFAULT_PASSWORD=${PGADMIN_PASSWORD} - PGADMIN_CONFIG_SERVER_MODE=False ports: - "${PGADMIN_PORT}:80" volumes: - ./infrastructure/pgadmin/servers.json:/pgadmin4/servers.json:ro networks: - bakery-network profiles: - development - admin redis-commander: image: rediscommander/redis-commander:latest container_name: bakery-redis-commander restart: unless-stopped environment: - REDIS_HOSTS=local:redis:6379:0:${REDIS_PASSWORD} - HTTP_USER=${REDIS_COMMANDER_USER} - HTTP_PASSWORD=${REDIS_COMMANDER_PASSWORD} ports: - "${REDIS_COMMANDER_PORT}:8081" networks: - bakery-network profiles: - development - admin depends_on: - redis