networks: otel-network: driver: bridge services: otel-collector: image: otel/opentelemetry-collector-contrib:latest # ≈ 50 MB container_name: otel-collector command: ["--config=/etc/otel/collector-config.yaml"] volumes: - ./collector-config.yaml:/etc/otel/collector-config.yaml:ro ports: - "4317:4317" # OTLP gRPC in - "4318:4318" # OTLP HTTP in - "8889:8889" # Prometheus scrape out restart: unless-stopped networks: - otel-network prometheus: image: prom/prometheus:latest container_name: prometheus volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro ports: - "9090:9090" restart: unless-stopped depends_on: [otel-collector] networks: - otel-network loki: image: grafana/loki:latest container_name: loki ports: - "3100:3100" command: -config.file=/etc/loki/local-config.yaml restart: unless-stopped networks: - otel-network grafana: image: grafana/grafana-oss:latest container_name: grafana environment: - GF_SECURITY_ADMIN_PASSWORD=admin - GF_USERS_ALLOW_SIGN_UP=false - GF_FEATURE_TOGGLES_ENABLE=logsSampleInExplore volumes: - ./grafana-datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml:ro - ./grafana-dashboards.yml:/etc/grafana/provisioning/dashboards/dashboards.yml:ro - ./claude-code-dashboard.json:/var/lib/grafana/dashboards/claude-code-dashboard.json:ro ports: - "3000:3000" restart: unless-stopped depends_on: [prometheus, loki] networks: - otel-network