# ============================================================================= # SmartNode — docker-compose.yml # # Usage: # docker compose up # foreground, rebuild if Dockerfile changed # docker compose up -d # detached # docker compose up --build -d # force rebuild then run detached # docker compose down # stop and remove containers # # # Start with full monitoring stack (Prometheus + Grafana + OTel Collector): # docker compose --profile monitoring up -d # # The service is available at http://localhost:5000/frontend/ # Health endpoint: http://localhost:5000/api/health # Metrics endpoint: http://localhost:5000/metrics # Prometheus UI: http://localhost:9090 (monitoring profile) # Grafana UI: http://localhost:3000 (monitoring profile, admin/admin) # ============================================================================= services: smartnode: build: context: . dockerfile: Dockerfile target: runtime image: smartnode:latest container_name: smartnode restart: unless-stopped ports: - "5000:5000" environment: # ---------------------------------------------------------------- # Application settings — override here or via a .env file # ---------------------------------------------------------------- SMARTNODE_ENV: "${SMARTNODE_ENV:-development}" SMARTNODE_HOST: "0.0.0.0" SMARTNODE_PORT: "5000" SMARTNODE_TIME_SCALE: "${SMARTNODE_TIME_SCALE:-10}" SMARTNODE_LOG_LEVEL: "${SMARTNODE_LOG_LEVEL:-INFO}" LOG_FORMAT: "${LOG_FORMAT:-json}" # ---------------------------------------------------------------- # Secrets — always supply real values in production # ---------------------------------------------------------------- SMARTNODE_JWT_SECRET: "${SMARTNODE_JWT_SECRET:-dev-insecure-secret-change-me}" SMARTNODE_API_KEY: "${SMARTNODE_API_KEY:-}" # ---------------------------------------------------------------- # gunicorn tuning # ---------------------------------------------------------------- GUNICORN_WORKERS: "${GUNICORN_WORKERS:-}" GUNICORN_TIMEOUT: "${GUNICORN_TIMEOUT:-120}" GUNICORN_LOG_LEVEL: "${GUNICORN_LOG_LEVEL:-info}" volumes: # Mount an optional config file without rebuilding the image. # Create a local smartnode.json with your overrides, or remove # this line if you manage all settings via environment variables. - "./config:/app/config:ro" healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5000/api/health')"] interval: 30s timeout: 10s retries: 3 start_period: 20s # Run as non-root user (matches Dockerfile UID/GID 1001) user: "1001:1001" # Security hardening security_opt: - no-new-privileges:true read_only: false tmpfs: - /tmp # --------------------------------------------------------------------------- # Monitoring profile — Prometheus + Grafana + OpenTelemetry Collector # Start with: docker compose --profile monitoring up -d # --------------------------------------------------------------------------- prometheus: image: prom/prometheus:v2.51.2 container_name: smartnode-prometheus profiles: ["monitoring"] restart: unless-stopped ports: - "9090:9090" volumes: - ./observability/prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus_data:/prometheus command: - "--config.file=/etc/prometheus/prometheus.yml" - "--storage.tsdb.path=/prometheus" - "--storage.tsdb.retention.time=7d" - "--web.console.libraries=/usr/share/prometheus/console_libraries" - "--web.console.templates=/usr/share/prometheus/consoles" - "--web.enable-lifecycle" depends_on: - smartnode grafana: image: grafana/grafana:10.4.2 container_name: smartnode-grafana profiles: ["monitoring"] restart: unless-stopped ports: - "3000:3000" environment: GF_SECURITY_ADMIN_USER: admin GF_SECURITY_ADMIN_PASSWORD: admin GF_USERS_ALLOW_SIGN_UP: "false" GF_AUTH_ANONYMOUS_ENABLED: "false" volumes: - grafana_data:/var/lib/grafana - ./observability/grafana-provisioning:/etc/grafana/provisioning:ro depends_on: - prometheus otel-collector: image: otel/opentelemetry-collector-contrib:0.100.0 container_name: smartnode-otel-collector profiles: ["monitoring"] restart: unless-stopped ports: - "4317:4317" # OTLP gRPC receiver - "4318:4318" # OTLP HTTP receiver - "8888:8888" # Collector metrics volumes: - ./observability/otel-collector.yml:/etc/otelcol-contrib/config.yaml:ro depends_on: - prometheus volumes: prometheus_data: {} grafana_data: {}