diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..ccbcc3d2 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,410 @@ + +services: + traefik: + image: traefik:2.11 + container_name: traefik + ports: + - "80:80" + - "443:443" + - "4433:4433" + # Prometheus metrics endpoint (local dev) + - "9100:9100" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./services/traefik/traefik.yml:/etc/traefik/traefik.yml:ro + - ./services/traefik/dynamic.yml:/etc/traefik/dynamic.yml:ro + - ./services/traefik/acme.json:/acme.json + - ./services/traefik/acme-io.json:/acme-io.json + labels: + - "traefik.enable=true" + # Dashboard over HTTPS (production) + - "traefik.http.routers.traefik.rule=Host(`traefik.truckwash.dk`)" + - "traefik.http.routers.traefik.entrypoints=websecure" + - "traefik.http.routers.traefik.tls=true" + - "traefik.http.routers.traefik.tls.certresolver=le" + - "traefik.http.routers.traefik.service=api@internal" + - "traefik.http.routers.traefik.middlewares=dashboard-allow-local@file,dashboard-auth@file" + # Dashboard over HTTP (dev) -> redirect to HTTPS + - "traefik.http.routers.traefik-http.rule=Host(`traefik.truckwash.dk`)" + - "traefik.http.routers.traefik-http.entrypoints=web" + - "traefik.http.routers.traefik-http.middlewares=redirect-to-https@file" + - "traefik.http.routers.traefik-http.service=api@internal" + # Local dashboard on traefik.localhost (HTTP only for dev) + - "traefik.http.routers.traefik-local.rule=Host(`traefik.localhost`)" + - "traefik.http.routers.traefik-local.entrypoints=web" + - "traefik.http.routers.traefik-local.service=api@internal" + - "traefik.http.routers.traefik-local.middlewares=dashboard-allow-local@file,dashboard-auth@file" + + redis: + image: redis:7 + container_name: redis +# ports: +# - "6379:6379" + volumes: + - nnks_redis:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + + redis-staging: + image: redis:7 + container_name: redis-staging +# ports: +# - "6380:6379" + volumes: + - nnks_redis_staging:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + + mysql-debug: + image: mysql:8.4 + container_name: mysql-debug + environment: + MYSQL_ROOT_PASSWORD: ${CONFIG_DB_DEBUG_PASSWORD:-debug_root_password} + MYSQL_DATABASE: ${CONFIG_DB_DEBUG_DATABASE:-nnks_db_debug} + ports: + - "3307:3306" + volumes: + - db_debug_data:/var/lib/mysql + healthcheck: + test: ["CMD-SHELL", "MYSQL_PWD=$$MYSQL_ROOT_PASSWORD mysqladmin -u root ping --silent"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 20s + + edge-broker: + build: + context: . + dockerfile: services/edge-broker/Dockerfile + container_name: edge-broker + environment: + EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev} + ports: + - "4300:4300" + + caddy: + image: caddy:2.7.6-alpine + container_name: caddy + depends_on: + - php1 + - php2 + - php3 + - php4 + - php5 + volumes: + - ./services/nginx/app:/var/www/html + - ./services/caddy/Caddyfile:/etc/caddy/Caddyfile:ro + - ./services/caddy/logs:/var/log/caddy + labels: + - "traefik.enable=true" + # Public API (HTTPS via Traefik + LE) + - "traefik.http.routers.api.rule=Host(`api.truckwash.dk`)" + - "traefik.http.routers.api.entrypoints=websecure" + - "traefik.http.routers.api.tls=true" + - "traefik.http.routers.api.tls.domains[0].main=api.truckwash.dk" + - "traefik.http.routers.api.tls.certresolver=le" + - "traefik.http.routers.api.service=caddy" + - "traefik.http.routers.api.middlewares=secure-headers@file,api-ratelimit@file" + # Public API (.io version) + - "traefik.http.routers.api-io.rule=Host(`api.truckwash.io`)" + - "traefik.http.routers.api-io.entrypoints=websecure" + - "traefik.http.routers.api-io.tls=true" + - "traefik.http.routers.api-io.tls.domains[0].main=api.truckwash.io" + - "traefik.http.routers.api-io.tls.certresolver=le_io" + - "traefik.http.routers.api-io.service=caddy" + - "traefik.http.routers.api-io.middlewares=secure-headers@file,api-ratelimit@file" + # HTTP to HTTPS redirect for both API domains + - "traefik.http.routers.api-http.rule=Host(`api.truckwash.dk`) || Host(`api.truckwash.io`)" + - "traefik.http.routers.api-http.entrypoints=web" + - "traefik.http.routers.api-http.middlewares=redirect-to-https@file" + - "traefik.http.routers.api-http.service=caddy" + # Local development (HTTP only) + - "traefik.http.routers.local.rule=Host(`localhost`)" + - "traefik.http.routers.local.entrypoints=web" + - "traefik.http.routers.local.service=caddy" + - "traefik.http.routers.local.middlewares=secure-headers@file" + # Local development over HTTPS (self-signed/default Traefik cert) + - "traefik.http.routers.local-secure.rule=Host(`localhost`)" + - "traefik.http.routers.local-secure.entrypoints=websecure" + - "traefik.http.routers.local-secure.tls=true" + - "traefik.http.routers.local-secure.service=caddy" + - "traefik.http.routers.local-secure.middlewares=secure-headers@file" + # Local alias: http://localhost/api -> Caddy (strip /api prefix) + - "traefik.http.routers.local-api.rule=Host(`localhost`) && PathPrefix(`/api`)" + - "traefik.http.routers.local-api.entrypoints=web" + - "traefik.http.routers.local-api.middlewares=strip-api-prefix@file,secure-headers@file" + - "traefik.http.routers.local-api.service=caddy" + - "traefik.http.routers.local-api.priority=100" + # Local alias over HTTPS as well: https://localhost/api -> Caddy (strip /api prefix) + - "traefik.http.routers.local-api-secure.rule=Host(`localhost`) && PathPrefix(`/api`)" + - "traefik.http.routers.local-api-secure.entrypoints=websecure" + - "traefik.http.routers.local-api-secure.tls=true" + - "traefik.http.routers.local-api-secure.middlewares=strip-api-prefix@file,secure-headers@file" + - "traefik.http.routers.local-api-secure.service=caddy" + - "traefik.http.routers.local-api-secure.priority=100" + # Tell Traefik which port Caddy listens on + - "traefik.http.services.caddy.loadbalancer.server.port=80" + + caddy-staging: + image: caddy:2.7.6-alpine + container_name: caddy-staging + depends_on: + - php-staging + volumes: + - ./services/nginx/staging:/var/www/html + - ./services/caddy/Caddyfile-staging:/etc/caddy/Caddyfile:ro + - ./services/caddy/logs-staging:/var/log/caddy + labels: + - "traefik.enable=true" + # Staging API (.io version on port 4433) + - "traefik.http.routers.api-staging.rule=Host(`api.truckwash.io`)" + - "traefik.http.routers.api-staging.entrypoints=websecure-staging" + - "traefik.http.routers.api-staging.tls=true" + - "traefik.http.routers.api-staging.tls.domains[0].main=api.truckwash.io" + - "traefik.http.routers.api-staging.tls.certresolver=le_io" + - "traefik.http.routers.api-staging.service=caddy-staging" + - "traefik.http.routers.api-staging.middlewares=secure-headers@file,api-ratelimit@file" + # Local staging development (HTTP on port 4433) + - "traefik.http.routers.local-staging.rule=Host(`localhost`)" + - "traefik.http.routers.local-staging.entrypoints=websecure-staging" + - "traefik.http.routers.local-staging.service=caddy-staging" + - "traefik.http.routers.local-staging.middlewares=secure-headers@file" + # Local staging development (HTTPS on port 4433) + - "traefik.http.routers.local-staging-secure.rule=Host(`localhost`)" + - "traefik.http.routers.local-staging-secure.entrypoints=websecure-staging" + - "traefik.http.routers.local-staging-secure.tls=true" + - "traefik.http.routers.local-staging-secure.service=caddy-staging" + - "traefik.http.routers.local-staging-secure.middlewares=secure-headers@file" + # Local staging alias: http://localhost:4433/api -> Caddy staging (strip /api prefix) + - "traefik.http.routers.local-staging-api.rule=Host(`localhost`) && PathPrefix(`/api`)" + - "traefik.http.routers.local-staging-api.entrypoints=websecure-staging" + - "traefik.http.routers.local-staging-api.middlewares=strip-api-prefix@file,secure-headers@file" + - "traefik.http.routers.local-staging-api.service=caddy-staging" + - "traefik.http.routers.local-staging-api.priority=100" + # Local staging alias over HTTPS: https://localhost:4433/api -> Caddy staging (strip /api prefix) + - "traefik.http.routers.local-staging-api-secure.rule=Host(`localhost`) && PathPrefix(`/api`)" + - "traefik.http.routers.local-staging-api-secure.entrypoints=websecure-staging" + - "traefik.http.routers.local-staging-api-secure.tls=true" + - "traefik.http.routers.local-staging-api-secure.middlewares=strip-api-prefix@file,secure-headers@file" + - "traefik.http.routers.local-staging-api-secure.service=caddy-staging" + - "traefik.http.routers.local-staging-api-secure.priority=100" + # Tell Traefik which port Caddy listens on + - "traefik.http.services.caddy-staging.loadbalancer.server.port=80" + + php1: + build: + context: . + dockerfile: services/php/Dockerfile + container_name: php1 + depends_on: + - redis + - edge-broker + command: ["php-fpm"] + env_file: + - .env + environment: + AUTO_COMPOSER_INSTALL: "true" + EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} + EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev} + volumes: + - ./services/nginx/app:/var/www/html + - ./services/edge-agent/dist:/services/edge-agent/dist:ro + - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro + - ./services/php/logs:/var/log/php + + php2: + build: + context: . + dockerfile: services/php/Dockerfile + container_name: php2 + depends_on: + - redis + - edge-broker + command: ["php-fpm"] + env_file: + - .env + environment: + AUTO_COMPOSER_INSTALL: "false" + EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} + EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev} + volumes: + - ./services/nginx/app:/var/www/html + - ./services/edge-agent/dist:/services/edge-agent/dist:ro + - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro + - ./services/php/logs:/var/log/php + + php3: + build: + context: . + dockerfile: services/php/Dockerfile + container_name: php3 + depends_on: + - redis + - edge-broker + command: ["php-fpm"] + env_file: + - .env + environment: + AUTO_COMPOSER_INSTALL: "false" + EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} + EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev} + volumes: + - ./services/nginx/app:/var/www/html + - ./services/edge-agent/dist:/services/edge-agent/dist:ro + - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro + - ./services/php/logs:/var/log/php + + php4: + build: + context: . + dockerfile: services/php/Dockerfile + container_name: php4 + depends_on: + - redis + - edge-broker + command: ["php-fpm"] + env_file: + - .env + environment: + AUTO_COMPOSER_INSTALL: "false" + EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} + EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev} + volumes: + - ./services/nginx/app:/var/www/html + - ./services/edge-agent/dist:/services/edge-agent/dist:ro + - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro + - ./services/php/logs:/var/log/php + + php5: + build: + context: . + dockerfile: services/php/Dockerfile + container_name: php5 + depends_on: + - redis + - edge-broker + command: ["php-fpm"] + env_file: + - .env + environment: + AUTO_COMPOSER_INSTALL: "false" + EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} + EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev} + volumes: + - ./services/nginx/app:/var/www/html + - ./services/edge-agent/dist:/services/edge-agent/dist:ro + - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro + - ./services/php/logs:/var/log/php + + php-staging: + build: + context: . + dockerfile: services/php/Dockerfile + container_name: php-staging + depends_on: + - redis-staging + - edge-broker + command: ["php-fpm"] + env_file: + - .env.staging + environment: + AUTO_COMPOSER_INSTALL: "false" + EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} + EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev} + volumes: + - ./services/nginx/staging:/var/www/html + - ./services/edge-agent/dist:/services/edge-agent/dist:ro + - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro + - ./services/php/logs-staging:/var/log/php + + php-cron: + build: + context: . + dockerfile: services/php/Dockerfile + container_name: php-cron + depends_on: + - redis + - edge-broker + command: ["sh", "-c", "while true; do php index.php run cron; sleep 60; done"] + env_file: + - .env + environment: + AUTO_COMPOSER_INSTALL: "false" + EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300} + EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev} + volumes: + - ./services/nginx/app:/var/www/html + - ./services/edge-agent/dist:/services/edge-agent/dist:ro + - ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro + - ./services/php/logs:/var/log/php + portainer: + image: portainer/portainer-ce:2.21.4 + container_name: portainer + profiles: + - dev + ports: + - "9443:9443" + - "9000:9000" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - portainer_data:/data + + # Jaeger all-in-one for local tracing (Traefik → Jaeger) + jaeger: + image: jaegertracing/all-in-one:1.53 + container_name: jaeger + profiles: + - dev + environment: + - COLLECTOR_ZIPKIN_HTTP_PORT=9411 + ports: + - "16686:16686" # Jaeger UI + # No volumes needed for dev; data is ephemeral + + n8n: + image: n8nio/n8n:latest + container_name: n8n + restart: always + environment: + - N8N_HOST=n8n.truckwash.io + - N8N_PORT=5678 + - N8N_PROTOCOL=https + - NODE_ENV=production + - WEBHOOK_URL=https://n8n.truckwash.io/ + - GENERIC_TIMEZONE=${CONFIG_TIMEZONE:-Europe/Copenhagen} + volumes: + - n8n_data:/home/node/.n8n + labels: + - "traefik.enable=true" + # n8n over HTTPS (le_io cert resolver) + - "traefik.http.routers.n8n.rule=Host(`n8n.truckwash.io`)" + - "traefik.http.routers.n8n.entrypoints=websecure" + - "traefik.http.routers.n8n.tls=true" + - "traefik.http.routers.n8n.tls.certresolver=le_io" + - "traefik.http.routers.n8n.service=n8n" + # n8n HTTP to HTTPS redirect + - "traefik.http.routers.n8n-http.rule=Host(`n8n.truckwash.io`)" + - "traefik.http.routers.n8n-http.entrypoints=web" + - "traefik.http.routers.n8n-http.middlewares=redirect-to-https@file" + - "traefik.http.routers.n8n-http.service=n8n" + # n8n service port + - "traefik.http.services.n8n.loadbalancer.server.port=5678" + +volumes: + db_data: + db_debug_data: + nnks_redis: + nnks_redis_staging: + es_data: + portainer_data: + fleet-server-data: + elastic-agent-data: + n8n_data: