Ctrl K
ComunidadGuías…

docker-compose.local.yml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# This is the local development environment for Tracecat.
services:
  caddy:
    image: caddy:2.10.2-alpine
    container_name: caddy
    restart: unless-stopped
    networks:
      - core
    ports:
      - ${PUBLIC_APP_PORT}:${PUBLIC_APP_PORT}
    environment:
      - BASE_DOMAIN=${BASE_DOMAIN}
      - ADDRESS=${ADDRESS}
      - TRACECAT__TRUSTED_PROXY_CIDRS=${TRACECAT__TRUSTED_PROXY_CIDRS:-127.0.0.1/32}
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile

  # All services use the production target from the multi-stage Dockerfile
  # to match the TRACECAT__APP_ENV=production environment variable
  api:
    build:
      context: .
      dockerfile: Dockerfile
      target: production
    container_name: api
    restart: unless-stopped
    networks:
      - core
      - core-db
    environment:
      # App
      TRACECAT__AUDIT_TRUSTED_PROXY_CIDRS: ${TRACECAT__AUDIT_TRUSTED_PROXY_CIDRS:-}
      TRACECAT__PLATFORM_ADMIN_IP_ALLOWLIST_BYPASS: ${TRACECAT__PLATFORM_ADMIN_IP_ALLOWLIST_BYPASS:-false}
      TRACECAT__PLATFORM_ADMIN_MFA_BYPASS: ${TRACECAT__PLATFORM_ADMIN_MFA_BYPASS:-false}
      LOG_LEVEL: ${LOG_LEVEL}
      TRACECAT__LOG_FORMAT: ${TRACECAT__LOG_FORMAT:-console}
      TRACECAT__PLATFORM_OTEL_ENABLED: ${TRACECAT__PLATFORM_OTEL_ENABLED:-false}
      OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://localhost:4318}
      OTEL_TRACES_SAMPLER: ${OTEL_TRACES_SAMPLER:-parentbased_traceidratio}
      OTEL_TRACES_SAMPLER_ARG: ${OTEL_TRACES_SAMPLER_ARG:-1.0}
      OTEL_EXPORTER_OTLP_HEADERS: ${OTEL_EXPORTER_OTLP_HEADERS:-}
      TRACECAT__ALLOW_ORIGINS: ${TRACECAT__ALLOW_ORIGINS}
      TRACECAT__API_ROOT_PATH: ${TRACECAT__API_ROOT_PATH}
      TRACECAT__API_URL: ${TRACECAT__API_URL}
      TRACECAT__APP_ENV: production
      TRACECAT__AWS_ASSUME_ROLE_ACCOUNT_ID: ${TRACECAT__AWS_ASSUME_ROLE_ACCOUNT_ID:-}
      TRACECAT__AWS_ASSUME_ROLE_PRINCIPAL_ARN: ${TRACECAT__AWS_ASSUME_ROLE_PRINCIPAL_ARN:-}
      TRACECAT__AUTH_ALLOWED_DOMAINS: ${TRACECAT__AUTH_ALLOWED_DOMAINS}
      TRACECAT__AUTH_MIN_PASSWORD_LENGTH: ${TRACECAT__AUTH_MIN_PASSWORD_LENGTH}
      TRACECAT__AUTH_TYPES: ${TRACECAT__AUTH_TYPES}
      TRACECAT__AUTH_SUPERADMIN_EMAIL: ${TRACECAT__AUTH_SUPERADMIN_EMAIL}
      TRACECAT__DEV_COOKIE_NAME: ${TRACECAT__DEV_COOKIE_NAME:-}
      TRACECAT__DB_ENCRYPTION_KEY: ${TRACECAT__DB_ENCRYPTION_KEY} # Sensitive
      TRACECAT__DB_SSLMODE: ${TRACECAT__DB_SSLMODE}
      TRACECAT__DB_URI: ${TRACECAT__DB_URI} # Sensitive
      TRACECAT__PUBLIC_API_URL: ${TRACECAT__PUBLIC_API_URL}
      TRACECAT__PUBLIC_APP_URL: ${TRACECAT__PUBLIC_APP_URL}
      TRACECAT__SMTP_HOST: ${TRACECAT__SMTP_HOST:-}
      TRACECAT__SMTP_PORT: ${TRACECAT__SMTP_PORT:-587}
      TRACECAT__SMTP_USER: ${TRACECAT__SMTP_USER:-}
      TRACECAT__SMTP_PASSWORD: ${TRACECAT__SMTP_PASSWORD:-} # Sensitive
      TRACECAT__EMAIL_FROM: ${TRACECAT__EMAIL_FROM:-}
      TRACECAT__SERVICE_KEY: ${TRACECAT__SERVICE_KEY} # Sensitive
      TRACECAT__SERVICE_NAME: api
      SENTRY_DSN: ${SENTRY_DSN:-}
      TRACECAT__AGENT_SANDBOX_TIMEOUT: ${TRACECAT__AGENT_SANDBOX_TIMEOUT:-10800}
      TRACECAT__DB_POOL_TIMEOUT: ${TRACECAT__DB_POOL_TIMEOUT:-30}
      TRACECAT__DB_AUTH_POOL_SIZE: ${TRACECAT__DB_AUTH_POOL_SIZE:-5}
      TRACECAT__DB_AUTH_MAX_OVERFLOW: ${TRACECAT__DB_AUTH_MAX_OVERFLOW:-5}
      TRACECAT__SIGNING_SECRET: ${TRACECAT__SIGNING_SECRET} # Sensitive
      OAUTH_CLIENT_ID: ${OAUTH_CLIENT_ID}
      OAUTH_CLIENT_SECRET: ${OAUTH_CLIENT_SECRET}
      OIDC_ISSUER: ${OIDC_ISSUER}
      OIDC_CLIENT_ID: ${OIDC_CLIENT_ID}
      OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET}
      OIDC_SCOPES: ${OIDC_SCOPES}
      USER_AUTH_SECRET: ${USER_AUTH_SECRET}
      TRACECAT__FEATURE_FLAGS: ${TRACECAT__FEATURE_FLAGS}
      TRACECAT__EE_MULTI_TENANT: ${TRACECAT__EE_MULTI_TENANT:-false}
      TRACECAT__LITELLM_BASE_URL: ${TRACECAT__LITELLM_BASE_URL:-http://litellm:4000}
      TRACECAT__OUTBOUND_ALLOWED_PRIVATE_CIDRS: ${TRACECAT__OUTBOUND_ALLOWED_PRIVATE_CIDRS:-}
      TRACECAT__CONTEXT_COMPRESSION_ENABLED: ${TRACECAT__CONTEXT_COMPRESSION_ENABLED:-false}
      TRACECAT__CONTEXT_COMPRESSION_THRESHOLD_KB: ${TRACECAT__CONTEXT_COMPRESSION_THRESHOLD_KB:-16}
      TRACECAT__RESULT_EXTERNALIZATION_ENABLED: ${TRACECAT__RESULT_EXTERNALIZATION_ENABLED:-true}
      TRACECAT__COLLECTION_MANIFESTS_ENABLED: ${TRACECAT__COLLECTION_MANIFESTS_ENABLED:-true}
      TRACECAT__RESULT_EXTERNALIZATION_THRESHOLD_BYTES: ${TRACECAT__RESULT_EXTERNALIZATION_THRESHOLD_BYTES}
      TRACECAT__REGISTRY_SYNC_SANDBOX_ENABLED: ${TRACECAT__REGISTRY_SYNC_SANDBOX_ENABLED:-true}
      TRACECAT__UNSAFE_DISABLE_SECRET_ERROR_WITHHOLDING: ${TRACECAT__UNSAFE_DISABLE_SECRET_ERROR_WITHHOLDING:-false}
      # SAML SSO
      SAML_IDP_METADATA_URL: ${SAML_IDP_METADATA_URL}
      # Temporal
      TEMPORAL__CLUSTER_URL: ${TEMPORAL__CLUSTER_URL}
      TRACECAT__BACKGROUND_QUEUE: ${TRACECAT__BACKGROUND_QUEUE:-shared-background-queue}
      TEMPORAL__CLUSTER_QUEUE: ${TEMPORAL__CLUSTER_QUEUE}
      TEMPORAL__CLUSTER_NAMESPACE: ${TEMPORAL__CLUSTER_NAMESPACE}
      TEMPORAL__API_KEY: ${TEMPORAL__API_KEY}
      TEMPORAL__PAYLOAD_ENCRYPTION_ENABLED: ${TEMPORAL__PAYLOAD_ENCRYPTION_ENABLED:-false}
      TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING: ${TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING:-}
      TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING_ARN: ${TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING_ARN:-}
      TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_TTL_SECONDS: ${TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_TTL_SECONDS:-3600}
      TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_MAX_ITEMS: ${TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_MAX_ITEMS:-128}
      # Blob Storage
      TRACECAT__BLOB_STORAGE_ENDPOINT: ${TRACECAT__BLOB_STORAGE_ENDPOINT:-http://minio:9000}
      TRACECAT__BLOB_STORAGE_SSL_VERIFY: ${TRACECAT__BLOB_STORAGE_SSL_VERIFY:-true}
      TRACECAT__BLOB_STORAGE_BUCKET_SKILLS: ${TRACECAT__BLOB_STORAGE_BUCKET_SKILLS:-tracecat-skills}
      TRACECAT__BLOB_STORAGE_PRESIGNED_URL_ENDPOINT: ${TRACECAT__BLOB_STORAGE_PRESIGNED_URL_ENDPOINT:-${TRACECAT__PUBLIC_APP_URL}/s3}
      TRACECAT__WORKFLOW_ARTIFACT_RETENTION_DAYS: ${TRACECAT__WORKFLOW_ARTIFACT_RETENTION_DAYS:-30}
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
      # Local registry
      TRACECAT__LOCAL_REPOSITORY_PATH: ${TRACECAT__LOCAL_REPOSITORY_PATH}
      TRACECAT__LOCAL_REPOSITORY_ENABLED: ${TRACECAT__LOCAL_REPOSITORY_ENABLED:-false}
      # Redis
      REDIS_URL: ${REDIS_URL}
    volumes:
      - ${TRACECAT__LOCAL_REPOSITORY_PATH}:/app/local_registry
    depends_on:
      migrations:
        condition: service_completed_successfully
      temporal:
        condition: service_healthy
      minio:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:$${PORT:-8000}/health"]
      interval: 5s
      timeout: 5s
      retries: 30
      start_period: 10s

  worker:
    build:
      context: .
      dockerfile: Dockerfile
      target: production
    restart: unless-stopped
    networks:
      - core
      - core-db
      - temporal
    environment:
      LOG_LEVEL: ${LOG_LEVEL}
      TRACECAT__LOG_FORMAT: ${TRACECAT__LOG_FORMAT:-console}
      TRACECAT__PLATFORM_OTEL_ENABLED: ${TRACECAT__PLATFORM_OTEL_ENABLED:-false}
      OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://localhost:4318}
      OTEL_TRACES_SAMPLER: ${OTEL_TRACES_SAMPLER:-parentbased_traceidratio}
      OTEL_TRACES_SAMPLER_ARG: ${OTEL_TRACES_SAMPLER_ARG:-1.0}
      OTEL_EXPORTER_OTLP_HEADERS: ${OTEL_EXPORTER_OTLP_HEADERS:-}
      TRACECAT__API_ROOT_PATH: ${TRACECAT__API_ROOT_PATH}
      TRACECAT__API_URL: ${TRACECAT__API_URL}
      TRACECAT__APP_ENV: production
      TRACECAT__AWS_ASSUME_ROLE_ACCOUNT_ID: ${TRACECAT__AWS_ASSUME_ROLE_ACCOUNT_ID:-}
      TRACECAT__AWS_ASSUME_ROLE_PRINCIPAL_ARN: ${TRACECAT__AWS_ASSUME_ROLE_PRINCIPAL_ARN:-}
      TRACECAT__DB_ENCRYPTION_KEY: ${TRACECAT__DB_ENCRYPTION_KEY} # Sensitive
      TRACECAT__DB_SSLMODE: ${TRACECAT__DB_SSLMODE}
      TRACECAT__DB_URI: ${TRACECAT__DB_URI} # Sensitive
      TRACECAT__PUBLIC_API_URL: ${TRACECAT__PUBLIC_API_URL}
      TRACECAT__PUBLIC_APP_URL: ${TRACECAT__PUBLIC_APP_URL}
      TRACECAT__SERVICE_KEY: ${TRACECAT__SERVICE_KEY} # Sensitive
      TRACECAT__SERVICE_NAME: worker
      TRACECAT__AGENT_SANDBOX_TIMEOUT: ${TRACECAT__AGENT_SANDBOX_TIMEOUT:-10800}
      TRACECAT__DB_POOL_TIMEOUT: ${TRACECAT__DB_POOL_TIMEOUT:-30}
      TRACECAT__DB_AUTH_POOL_SIZE: ${TRACECAT__DB_AUTH_POOL_SIZE:-5}
      TRACECAT__DB_AUTH_MAX_OVERFLOW: ${TRACECAT__DB_AUTH_MAX_OVERFLOW:-5}
      TRACECAT__SIGNING_SECRET: ${TRACECAT__SIGNING_SECRET} # Sensitive
      TRACECAT__FEATURE_FLAGS: ${TRACECAT__FEATURE_FLAGS}
      TRACECAT__EE_MULTI_TENANT: ${TRACECAT__EE_MULTI_TENANT:-false}
      TRACECAT__CONTEXT_COMPRESSION_ENABLED: ${TRACECAT__CONTEXT_COMPRESSION_ENABLED:-false}
      TRACECAT__CONTEXT_COMPRESSION_THRESHOLD_KB: ${TRACECAT__CONTEXT_COMPRESSION_THRESHOLD_KB:-16}
      TRACECAT__RESULT_EXTERNALIZATION_ENABLED: ${TRACECAT__RESULT_EXTERNALIZATION_ENABLED:-true}
      TRACECAT__COLLECTION_MANIFESTS_ENABLED: ${TRACECAT__COLLECTION_MANIFESTS_ENABLED:-true}
      TRACECAT__RESULT_EXTERNALIZATION_THRESHOLD_BYTES: ${TRACECAT__RESULT_EXTERNALIZATION_THRESHOLD_BYTES}
      TRACECAT__UNSAFE_DISABLE_SECRET_ERROR_WITHHOLDING: ${TRACECAT__UNSAFE_DISABLE_SECRET_ERROR_WITHHOLDING:-false}
      # Temporal
      TEMPORAL__CLUSTER_URL: ${TEMPORAL__CLUSTER_URL}
      TRACECAT__BACKGROUND_QUEUE: ${TRACECAT__BACKGROUND_QUEUE:-shared-background-queue}
      TEMPORAL__CLUSTER_QUEUE: ${TEMPORAL__CLUSTER_QUEUE}
      TEMPORAL__CLUSTER_NAMESPACE: ${TEMPORAL__CLUSTER_NAMESPACE}
      TEMPORAL__API_KEY: ${TEMPORAL__API_KEY}
      TEMPORAL__PAYLOAD_ENCRYPTION_ENABLED: ${TEMPORAL__PAYLOAD_ENCRYPTION_ENABLED:-false}
      TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING: ${TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING:-}
      TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING_ARN: ${TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING_ARN:-}
      TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_TTL_SECONDS: ${TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_TTL_SECONDS:-3600}
      TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_MAX_ITEMS: ${TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_MAX_ITEMS:-128}
      # Worker concurrency tuning
      TEMPORAL__THREADPOOL_MAX_WORKERS: ${TEMPORAL__THREADPOOL_MAX_WORKERS:-100}
      TEMPORAL__MAX_CONCURRENT_ACTIVITIES: ${TEMPORAL__MAX_CONCURRENT_ACTIVITIES:-100}
      TEMPORAL__MAX_CONCURRENT_WORKFLOW_TASKS: ${TEMPORAL__MAX_CONCURRENT_WORKFLOW_TASKS:-100}
      # Local registry
      TRACECAT__LOCAL_REPOSITORY_PATH: ${TRACECAT__LOCAL_REPOSITORY_PATH}
      TRACECAT__LOCAL_REPOSITORY_ENABLED: ${TRACECAT__LOCAL_REPOSITORY_ENABLED:-false}
      TRACECAT__EXECUTOR_CLIENT_TIMEOUT: ${TRACECAT__EXECUTOR_CLIENT_TIMEOUT:-300}
      # Sentry
      SENTRY_DSN: ${SENTRY_DSN:-}
      # Redis
      REDIS_URL: ${REDIS_URL}
      # Blob storage (for S3)
      TRACECAT__BLOB_STORAGE_ENDPOINT: ${TRACECAT__BLOB_STORAGE_ENDPOINT:-http://minio:9000}
      TRACECAT__BLOB_STORAGE_SSL_VERIFY: ${TRACECAT__BLOB_STORAGE_SSL_VERIFY:-true}
      TRACECAT__BLOB_STORAGE_BUCKET_SKILLS: ${TRACECAT__BLOB_STORAGE_BUCKET_SKILLS:-tracecat-skills}
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
    command: ["python", "-m", "tracecat.dsl.worker"]
    depends_on:
      migrations:
        condition: service_completed_successfully
      api:
        condition: service_started
      temporal:
        condition: service_healthy
      redis:
        condition: service_healthy
      minio:
        condition: service_healthy

  background-worker:
    build:
      context: .
      dockerfile: Dockerfile
      target: production
    restart: unless-stopped
    networks:
      - core
      - core-db
      - temporal
    environment:
      TRACECAT__BACKGROUND_MAX_CONCURRENT_ACTIVITIES: ${TRACECAT__BACKGROUND_MAX_CONCURRENT_ACTIVITIES:-10}
      TRACECAT__AGENT_SANDBOX_TIMEOUT: ${TRACECAT__AGENT_SANDBOX_TIMEOUT:-10800}
      TRACECAT__CONTEXT_COMPRESSION_ENABLED: ${TRACECAT__CONTEXT_COMPRESSION_ENABLED:-false}
      TRACECAT__CONTEXT_COMPRESSION_THRESHOLD_KB: ${TRACECAT__CONTEXT_COMPRESSION_THRESHOLD_KB:-16}
      LOG_LEVEL: ${LOG_LEVEL}
      TRACECAT__LOG_FORMAT: ${TRACECAT__LOG_FORMAT:-console}
      TRACECAT__PLATFORM_OTEL_ENABLED: ${TRACECAT__PLATFORM_OTEL_ENABLED:-false}
      OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://localhost:4318}
      OTEL_TRACES_SAMPLER: ${OTEL_TRACES_SAMPLER:-parentbased_traceidratio}
      OTEL_TRACES_SAMPLER_ARG: ${OTEL_TRACES_SAMPLER_ARG:-1.0}
      OTEL_EXPORTER_OTLP_HEADERS: ${OTEL_EXPORTER_OTLP_HEADERS:-}
      TRACECAT__API_ROOT_PATH: ${TRACECAT__API_ROOT_PATH}
      TRACECAT__API_URL: ${TRACECAT__API_URL}
      TRACECAT__APP_ENV: production
      TRACECAT__AWS_ASSUME_ROLE_ACCOUNT_ID: ${TRACECAT__AWS_ASSUME_ROLE_ACCOUNT_ID:-}
      TRACECAT__AWS_ASSUME_ROLE_PRINCIPAL_ARN: ${TRACECAT__AWS_ASSUME_ROLE_PRINCIPAL_ARN:-}
      TRACECAT__DB_ENCRYPTION_KEY: ${TRACECAT__DB_ENCRYPTION_KEY} # Sensitive
      TRACECAT__DB_SSLMODE: ${TRACECAT__DB_SSLMODE}
      TRACECAT__DB_URI: ${TRACECAT__DB_URI} # Sensitive
      TRACECAT__PUBLIC_API_URL: ${TRACECAT__PUBLIC_API_URL}
      TRACECAT__PUBLIC_APP_URL: ${TRACECAT__PUBLIC_APP_URL}
      TRACECAT__SERVICE_KEY: ${TRACECAT__SERVICE_KEY} # Sensitive
      TRACECAT__SERVICE_NAME: background-worker
      # Embedding provider calls from semantic-search indexing.
      TRACECAT__OUTBOUND_ALLOWED_PRIVATE_CIDRS: ${TRACECAT__OUTBOUND_ALLOWED_PRIVATE_CIDRS:-}
      TRACECAT__DB_POOL_TIMEOUT: ${TRACECAT__DB_POOL_TIMEOUT:-30}
      TRACECAT__DB_AUTH_POOL_SIZE: ${TRACECAT__DB_AUTH_POOL_SIZE:-5}
      TRACECAT__DB_AUTH_MAX_OVERFLOW: ${TRACECAT__DB_AUTH_MAX_OVERFLOW:-5}
      TRACECAT__SIGNING_SECRET: ${TRACECAT__SIGNING_SECRET} # Sensitive
      TRACECAT__FEATURE_FLAGS: ${TRACECAT__FEATURE_FLAGS}
      TRACECAT__EE_MULTI_TENANT: ${TRACECAT__EE_MULTI_TENANT:-false}
      TRACECAT__RESULT_EXTERNALIZATION_ENABLED: ${TRACECAT__RESULT_EXTERNALIZATION_ENABLED:-true}
      TRACECAT__COLLECTION_MANIFESTS_ENABLED: ${TRACECAT__COLLECTION_MANIFESTS_ENABLED:-true}
      TRACECAT__RESULT_EXTERNALIZATION_THRESHOLD_BYTES: ${TRACECAT__RESULT_EXTERNALIZATION_THRESHOLD_BYTES}
      TRACECAT__UNSAFE_DISABLE_SECRET_ERROR_WITHHOLDING: ${TRACECAT__UNSAFE_DISABLE_SECRET_ERROR_WITHHOLDING:-false}
      # Temporal
      TEMPORAL__CLUSTER_URL: ${TEMPORAL__CLUSTER_URL}
      TRACECAT__BACKGROUND_QUEUE: ${TRACECAT__BACKGROUND_QUEUE:-shared-background-queue}
      TEMPORAL__CLUSTER_QUEUE: ${TEMPORAL__CLUSTER_QUEUE}
      TEMPORAL__CLUSTER_NAMESPACE: ${TEMPORAL__CLUSTER_NAMESPACE}
      TEMPORAL__API_KEY: ${TEMPORAL__API_KEY}
      TEMPORAL__PAYLOAD_ENCRYPTION_ENABLED: ${TEMPORAL__PAYLOAD_ENCRYPTION_ENABLED:-false}
      TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING: ${TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING:-}
      TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING_ARN: ${TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING_ARN:-}
      TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_TTL_SECONDS: ${TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_TTL_SECONDS:-3600}
      TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_MAX_ITEMS: ${TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_MAX_ITEMS:-128}
      # Local registry
      TRACECAT__LOCAL_REPOSITORY_PATH: ${TRACECAT__LOCAL_REPOSITORY_PATH}
      TRACECAT__LOCAL_REPOSITORY_ENABLED: ${TRACECAT__LOCAL_REPOSITORY_ENABLED:-false}
      # Sentry
      SENTRY_DSN: ${SENTRY_DSN:-}
      # Redis
      REDIS_URL: ${REDIS_URL}
      # Blob storage (for S3)
      TRACECAT__BLOB_STORAGE_ENDPOINT: ${TRACECAT__BLOB_STORAGE_ENDPOINT:-http://minio:9000}
      TRACECAT__BLOB_STORAGE_SSL_VERIFY: ${TRACECAT__BLOB_STORAGE_SSL_VERIFY:-true}
      TRACECAT__BLOB_STORAGE_BUCKET_SKILLS: ${TRACECAT__BLOB_STORAGE_BUCKET_SKILLS:-tracecat-skills}
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
    command: ["python", "-m", "tracecat.background.worker"]
    depends_on:
      migrations:
        condition: service_completed_successfully
      api:
        condition: service_started
      temporal:
        condition: service_healthy
      redis:
        condition: service_healthy
      minio:
        condition: service_healthy

  executor:
    build:
      context: .
      dockerfile: Dockerfile
      target: production
    restart: unless-stopped

    # Container-level fork-bomb bound: in-jail RLIMIT_NPROC is bypassable
    # via nested user namespaces (fresh per-userns fork counter).
    # All activities, loop iterations, and executor overhead share this cap.
    # Defaults allow 32 activities x 4 for_each iterations x 64 in-jail
    # processes = 8192, exceeding this intentional aggregate safety limit.
    # Process-heavy workloads can hit the cap and fail to fork.
    pids_limit: 4096
    networks:
      - core
      - core-db
      - temporal
    # For nsjail, use docker-compose.sandbox.yml with this file.
    # The overlay supplies the required privileges and cgroup bootstrap.
    environment:
      # Common
      LOG_LEVEL: ${LOG_LEVEL}
      TRACECAT__LOG_FORMAT: ${TRACECAT__LOG_FORMAT:-console}
      TRACECAT__PLATFORM_OTEL_ENABLED: ${TRACECAT__PLATFORM_OTEL_ENABLED:-false}
      OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://localhost:4318}
      OTEL_TRACES_SAMPLER: ${OTEL_TRACES_SAMPLER:-parentbased_traceidratio}
      OTEL_TRACES_SAMPLER_ARG: ${OTEL_TRACES_SAMPLER_ARG:-1.0}
      TRACECAT__APP_ENV: production
      TRACECAT__AWS_ASSUME_ROLE_ACCOUNT_ID: ${TRACECAT__AWS_ASSUME_ROLE_ACCOUNT_ID:-}
      TRACECAT__AWS_ASSUME_ROLE_PRINCIPAL_ARN: ${TRACECAT__AWS_ASSUME_ROLE_PRINCIPAL_ARN:-}
      TRACECAT__DB_ENCRYPTION_KEY: ${TRACECAT__DB_ENCRYPTION_KEY} # Sensitive
      TRACECAT__DB_SSLMODE: ${TRACECAT__DB_SSLMODE}
      TRACECAT__DB_URI: ${TRACECAT__DB_URI} # Sensitive
      TRACECAT__SERVICE_KEY: ${TRACECAT__SERVICE_KEY} # Sensitive
      TRACECAT__SERVICE_NAME: executor
      TRACECAT__AGENT_SANDBOX_TIMEOUT: ${TRACECAT__AGENT_SANDBOX_TIMEOUT:-10800}
      TRACECAT__DB_POOL_TIMEOUT: ${TRACECAT__DB_POOL_TIMEOUT:-30}
      TRACECAT__DB_AUTH_POOL_SIZE: ${TRACECAT__DB_AUTH_POOL_SIZE:-5}
      TRACECAT__DB_AUTH_MAX_OVERFLOW: ${TRACECAT__DB_AUTH_MAX_OVERFLOW:-5}
      TRACECAT__SIGNING_SECRET: ${TRACECAT__SIGNING_SECRET} # Sensitive
      TRACECAT__FEATURE_FLAGS: ${TRACECAT__FEATURE_FLAGS}
      TRACECAT__EE_MULTI_TENANT: ${TRACECAT__EE_MULTI_TENANT:-false}
      TRACECAT__API_URL: ${TRACECAT__API_URL:-http://api:8000}
      # Compression (must match worker for codec compatibility)
      TRACECAT__CONTEXT_COMPRESSION_ENABLED: ${TRACECAT__CONTEXT_COMPRESSION_ENABLED:-false}
      TRACECAT__CONTEXT_COMPRESSION_THRESHOLD_KB: ${TRACECAT__CONTEXT_COMPRESSION_THRESHOLD_KB:-16}
      TRACECAT__RESULT_EXTERNALIZATION_ENABLED: ${TRACECAT__RESULT_EXTERNALIZATION_ENABLED:-true}
      TRACECAT__COLLECTION_MANIFESTS_ENABLED: ${TRACECAT__COLLECTION_MANIFESTS_ENABLED:-true}
      TRACECAT__RESULT_EXTERNALIZATION_THRESHOLD_BYTES: ${TRACECAT__RESULT_EXTERNALIZATION_THRESHOLD_BYTES}
      # Registry
      TRACECAT__EXECUTOR_REGISTRY_CACHE_MAX_ENTRIES: ${TRACECAT__EXECUTOR_REGISTRY_CACHE_MAX_ENTRIES:-64}
      TRACECAT__EXECUTOR_REGISTRY_CACHE_MAX_BYTES: ${TRACECAT__EXECUTOR_REGISTRY_CACHE_MAX_BYTES:-10737418240}
      TRACECAT__UNSAFE_DISABLE_SM_MASKING: ${TRACECAT__UNSAFE_DISABLE_SM_MASKING:-false}
      TRACECAT__UNSAFE_DISABLE_SECRET_ERROR_WITHHOLDING: ${TRACECAT__UNSAFE_DISABLE_SECRET_ERROR_WITHHOLDING:-false}
      # Local registry
      TRACECAT__LOCAL_REPOSITORY_PATH: ${TRACECAT__LOCAL_REPOSITORY_PATH}
      TRACECAT__LOCAL_REPOSITORY_ENABLED: ${TRACECAT__LOCAL_REPOSITORY_ENABLED:-false}
      PYTHONPATH: ${PYTHONPATH:+${PYTHONPATH}:}/home/apiuser/.local:/app/local_registry
      # Sandbox
      TRACECAT__SANDBOX_DEFAULT_MEMORY_MB: ${TRACECAT__SANDBOX_DEFAULT_MEMORY_MB:-2048}
      TRACECAT__SANDBOX_ADDRESS_SPACE_MB: ${TRACECAT__SANDBOX_ADDRESS_SPACE_MB:-}
      TRACECAT__SANDBOX_NSJAIL_PATH: /usr/local/bin/nsjail
      TRACECAT__SANDBOX_ROOTFS_PATH: /var/lib/tracecat/sandbox-rootfs
      TRACECAT__SANDBOX_CACHE_DIR: /var/lib/tracecat/sandbox-cache
      TRACECAT__REGISTRY_SYNC_CLONE_TIMEOUT: ${TRACECAT__REGISTRY_SYNC_CLONE_TIMEOUT:-120}
      TRACECAT__SANDBOX_INSTALL_ALLOWED_EGRESS_CIDRS: ${TRACECAT__SANDBOX_INSTALL_ALLOWED_EGRESS_CIDRS:-}
      TRACECAT__SANDBOX_INSTALL_ALLOWED_EGRESS_TCP_PORTS: ${TRACECAT__SANDBOX_INSTALL_ALLOWED_EGRESS_TCP_PORTS:-80,443}
      TRACECAT__SANDBOX_REGISTRY_ALLOWED_EGRESS_CIDRS: ${TRACECAT__SANDBOX_REGISTRY_ALLOWED_EGRESS_CIDRS:-}
      TRACECAT__SANDBOX_REGISTRY_ALLOWED_EGRESS_TCP_PORTS: ${TRACECAT__SANDBOX_REGISTRY_ALLOWED_EGRESS_TCP_PORTS:-22}
      TRACECAT__SANDBOX_SCRIPT_ALLOWED_EGRESS_CIDRS: ${TRACECAT__SANDBOX_SCRIPT_ALLOWED_EGRESS_CIDRS:-}
      TRACECAT__SANDBOX_SCRIPT_ALLOWED_EGRESS_TCP_PORTS: ${TRACECAT__SANDBOX_SCRIPT_ALLOWED_EGRESS_TCP_PORTS:-443}
      TRACECAT__SANDBOX_ACTION_ALLOWED_EGRESS_CIDRS: ${TRACECAT__SANDBOX_ACTION_ALLOWED_EGRESS_CIDRS:-}
      TRACECAT__SANDBOX_ACTION_ALLOWED_EGRESS_TCP_PORTS: ${TRACECAT__SANDBOX_ACTION_ALLOWED_EGRESS_TCP_PORTS:-443}
      TRACECAT__SANDBOX_AGENT_ALLOWED_EGRESS_CIDRS: ${TRACECAT__SANDBOX_AGENT_ALLOWED_EGRESS_CIDRS:-}
      TRACECAT__SANDBOX_AGENT_ALLOWED_EGRESS_TCP_PORTS: ${TRACECAT__SANDBOX_AGENT_ALLOWED_EGRESS_TCP_PORTS:-443}
      TRACECAT__SANDBOX_BLOCKED_EGRESS_CIDRS: ${TRACECAT__SANDBOX_BLOCKED_EGRESS_CIDRS:-}
      TRACECAT__SANDBOX_DNS_ALLOWED_EGRESS_CIDRS: ${TRACECAT__SANDBOX_DNS_ALLOWED_EGRESS_CIDRS:-}
      TRACECAT__SANDBOX_ALLOW_PUBLIC_IPV6_EGRESS: ${TRACECAT__SANDBOX_ALLOW_PUBLIC_IPV6_EGRESS:-false}
      # Temporal
      TEMPORAL__CLUSTER_URL: ${TEMPORAL__CLUSTER_URL}
      TEMPORAL__CLUSTER_NAMESPACE: ${TEMPORAL__CLUSTER_NAMESPACE}
      TEMPORAL__API_KEY: ${TEMPORAL__API_KEY}
      TEMPORAL__PAYLOAD_ENCRYPTION_ENABLED: ${TEMPORAL__PAYLOAD_ENCRYPTION_ENABLED:-false}
      TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING: ${TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING:-}
      TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING_ARN: ${TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING_ARN:-}
      TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_TTL_SECONDS: ${TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_TTL_SECONDS:-3600}
      TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_MAX_ITEMS: ${TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_MAX_ITEMS:-128}
      # Executor worker configuration
      TRACECAT__EXECUTOR_QUEUE: ${TRACECAT__EXECUTOR_QUEUE:-shared-action-queue}
      TRACECAT__EXECUTOR_BACKEND: ${TRACECAT__EXECUTOR_BACKEND:-direct}
      TRACECAT__EXECUTOR_MAX_CONCURRENT_ACTIVITIES: ${TRACECAT__EXECUTOR_MAX_CONCURRENT_ACTIVITIES:-32}
      TRACECAT__EXECUTOR_THREADPOOL_MAX_WORKERS: ${TRACECAT__EXECUTOR_THREADPOOL_MAX_WORKERS:-32}
      TRACECAT__EXECUTOR_FOR_EACH_MAX_CONCURRENCY: ${TRACECAT__EXECUTOR_FOR_EACH_MAX_CONCURRENCY:-4}
      # Sentry
      SENTRY_DSN: ${SENTRY_DSN:-}
      # Redis
      REDIS_URL: ${REDIS_URL}
      # Blob storage (for S3/MinIO wheel access)
      TRACECAT__BLOB_STORAGE_ENDPOINT: ${TRACECAT__BLOB_STORAGE_ENDPOINT:-http://minio:9000}
      TRACECAT__BLOB_STORAGE_SSL_VERIFY: ${TRACECAT__BLOB_STORAGE_SSL_VERIFY:-true}
      TRACECAT__BLOB_STORAGE_BUCKET_SKILLS: ${TRACECAT__BLOB_STORAGE_BUCKET_SKILLS:-tracecat-skills}
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
    volumes:
      - ${TRACECAT__LOCAL_REPOSITORY_PATH}:/app/local_registry
      - sandbox-cache:/var/lib/tracecat/sandbox-cache
    command: ["python", "-m", "tracecat.executor.worker"]
    depends_on:
      migrations:
        condition: service_completed_successfully
      temporal:
        condition: service_healthy
      redis:
        condition: service_healthy
      minio:
        condition: service_healthy

  agent-worker:
    build:
      context: .
      dockerfile: Dockerfile
      target: production
    restart: unless-stopped
    networks:
      - core
      - core-db
      - temporal
    environment:
      # Common
      LOG_LEVEL: ${LOG_LEVEL}
      TRACECAT__LOG_FORMAT: ${TRACECAT__LOG_FORMAT:-console}
      TRACECAT__PLATFORM_OTEL_ENABLED: ${TRACECAT__PLATFORM_OTEL_ENABLED:-false}
      OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://localhost:4318}
      OTEL_TRACES_SAMPLER: ${OTEL_TRACES_SAMPLER:-parentbased_traceidratio}
      OTEL_TRACES_SAMPLER_ARG: ${OTEL_TRACES_SAMPLER_ARG:-1.0}
      OTEL_EXPORTER_OTLP_HEADERS: ${OTEL_EXPORTER_OTLP_HEADERS:-}
      TRACECAT__APP_ENV: production
      TRACECAT__AWS_ASSUME_ROLE_ACCOUNT_ID: ${TRACECAT__AWS_ASSUME_ROLE_ACCOUNT_ID:-}
      TRACECAT__AWS_ASSUME_ROLE_PRINCIPAL_ARN: ${TRACECAT__AWS_ASSUME_ROLE_PRINCIPAL_ARN:-}
      TRACECAT__DB_ENCRYPTION_KEY: ${TRACECAT__DB_ENCRYPTION_KEY} # Sensitive
      TRACECAT__DB_SSLMODE: ${TRACECAT__DB_SSLMODE}
      TRACECAT__DB_URI: ${TRACECAT__DB_URI} # Sensitive
      TRACECAT__SERVICE_KEY: ${TRACECAT__SERVICE_KEY} # Sensitive
      TRACECAT__SERVICE_NAME: agent-worker
      TRACECAT__AGENT_SANDBOX_TIMEOUT: ${TRACECAT__AGENT_SANDBOX_TIMEOUT:-10800}
      TRACECAT__DB_POOL_TIMEOUT: ${TRACECAT__DB_POOL_TIMEOUT:-30}
      TRACECAT__DB_AUTH_POOL_SIZE: ${TRACECAT__DB_AUTH_POOL_SIZE:-5}
      TRACECAT__DB_AUTH_MAX_OVERFLOW: ${TRACECAT__DB_AUTH_MAX_OVERFLOW:-5}
      TRACECAT__SIGNING_SECRET: ${TRACECAT__SIGNING_SECRET} # Sensitive
      TRACECAT__FEATURE_FLAGS: ${TRACECAT__FEATURE_FLAGS}
      TRACECAT__EE_MULTI_TENANT: ${TRACECAT__EE_MULTI_TENANT:-false}
      TRACECAT__API_URL: ${TRACECAT__API_URL:-http://api:8000}
      # Compression (must match worker for codec compatibility)
      TRACECAT__CONTEXT_COMPRESSION_ENABLED: ${TRACECAT__CONTEXT_COMPRESSION_ENABLED:-false}
      TRACECAT__CONTEXT_COMPRESSION_THRESHOLD_KB: ${TRACECAT__CONTEXT_COMPRESSION_THRESHOLD_KB:-16}
      TRACECAT__RESULT_EXTERNALIZATION_ENABLED: ${TRACECAT__RESULT_EXTERNALIZATION_ENABLED:-true}
      TRACECAT__COLLECTION_MANIFESTS_ENABLED: ${TRACECAT__COLLECTION_MANIFESTS_ENABLED:-true}
      TRACECAT__RESULT_EXTERNALIZATION_THRESHOLD_BYTES: ${TRACECAT__RESULT_EXTERNALIZATION_THRESHOLD_BYTES}
      # Registry
      TRACECAT__UNSAFE_DISABLE_SM_MASKING: ${TRACECAT__UNSAFE_DISABLE_SM_MASKING:-false}
      TRACECAT__UNSAFE_DISABLE_SECRET_ERROR_WITHHOLDING: ${TRACECAT__UNSAFE_DISABLE_SECRET_ERROR_WITHHOLDING:-false}
      # Temporal
      TEMPORAL__CLUSTER_URL: ${TEMPORAL__CLUSTER_URL}
      TEMPORAL__CLUSTER_NAMESPACE: ${TEMPORAL__CLUSTER_NAMESPACE}
      TEMPORAL__API_KEY: ${TEMPORAL__API_KEY}
      TEMPORAL__PAYLOAD_ENCRYPTION_ENABLED: ${TEMPORAL__PAYLOAD_ENCRYPTION_ENABLED:-false}
      TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING: ${TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING:-}
      TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING_ARN: ${TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING_ARN:-}
      TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_TTL_SECONDS: ${TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_TTL_SECONDS:-3600}
      TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_MAX_ITEMS: ${TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_MAX_ITEMS:-128}
      SENTRY_DSN: ${SENTRY_DSN:-}
      # Agent worker configuration
      TRACECAT__AGENT_QUEUE: ${TRACECAT__AGENT_QUEUE:-shared-agent-queue}
      TRACECAT__AGENT_EXECUTOR_QUEUE: ${TRACECAT__AGENT_EXECUTOR_QUEUE:-shared-agent-executor-queue}
      TRACECAT__EXECUTOR_QUEUE: ${TRACECAT__EXECUTOR_QUEUE:-shared-action-queue}
      TRACECAT__AGENT_MAX_CONCURRENT_ACTIVITIES: ${TRACECAT__AGENT_MAX_CONCURRENT_ACTIVITIES:-100}
      TEMPORAL__THREADPOOL_MAX_WORKERS: ${TEMPORAL__THREADPOOL_MAX_WORKERS:-100}
      TRACECAT__EXECUTOR_CLIENT_TIMEOUT: ${TRACECAT__EXECUTOR_CLIENT_TIMEOUT:-300}
      TRACECAT__LITELLM_BASE_URL: ${TRACECAT__LITELLM_BASE_URL:-http://litellm:4000}
      TRACECAT__OUTBOUND_ALLOWED_PRIVATE_CIDRS: ${TRACECAT__OUTBOUND_ALLOWED_PRIVATE_CIDRS:-}
      # Redis
      REDIS_URL: ${REDIS_URL}
      # Blob storage (for S3/MinIO tarball access)
      TRACECAT__BLOB_STORAGE_ENDPOINT: ${TRACECAT__BLOB_STORAGE_ENDPOINT:-http://minio:9000}
      TRACECAT__BLOB_STORAGE_SSL_VERIFY: ${TRACECAT__BLOB_STORAGE_SSL_VERIFY:-true}
      TRACECAT__BLOB_STORAGE_BUCKET_SKILLS: ${TRACECAT__BLOB_STORAGE_BUCKET_SKILLS:-tracecat-skills}
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
    command: ["python", "-m", "tracecat.agent.worker"]
    depends_on:
      migrations:
        condition: service_completed_successfully
      temporal:
        condition: service_healthy
      redis:
        condition: service_healthy
      minio:
        condition: service_healthy

  litellm:
    build:
      context: .
      dockerfile: Dockerfile
      target: production
    restart: unless-stopped
    networks:
      - core
      - core-db
    environment:
      LOG_LEVEL: ${LOG_LEVEL}
      TRACECAT__LOG_FORMAT: ${TRACECAT__LOG_FORMAT:-console}
      TRACECAT__APP_ENV: production
      TRACECAT__DB_ENCRYPTION_KEY: ${TRACECAT__DB_ENCRYPTION_KEY} # Sensitive
      TRACECAT__DB_SSLMODE: ${TRACECAT__DB_SSLMODE}
      TRACECAT__DB_URI: ${TRACECAT__DB_URI} # Sensitive
      TRACECAT__SERVICE_KEY: ${TRACECAT__SERVICE_KEY} # Sensitive
      TRACECAT__SERVICE_NAME: litellm
      TRACECAT__DB_POOL_TIMEOUT: ${TRACECAT__DB_POOL_TIMEOUT:-30}
      TRACECAT__DB_AUTH_POOL_SIZE: ${TRACECAT__DB_AUTH_POOL_SIZE:-5}
      TRACECAT__DB_AUTH_MAX_OVERFLOW: ${TRACECAT__DB_AUTH_MAX_OVERFLOW:-5}
      TRACECAT__LITELLM_PORT: ${TRACECAT__LITELLM_PORT:-4000}
      TRACECAT__LITELLM_BASE_URL: ${TRACECAT__LITELLM_BASE_URL:-http://litellm:4000}
      TRACECAT__OUTBOUND_ALLOWED_PRIVATE_CIDRS: ${TRACECAT__OUTBOUND_ALLOWED_PRIVATE_CIDRS:-}
      PYTHONPATH: /app:/app/packages/tracecat-registry:/app/packages/tracecat-ee
    command: ["python", "-m", "tracecat.agent.litellm"]
    depends_on:
      migrations:
        condition: service_completed_successfully
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:$${TRACECAT__LITELLM_PORT:-4000}/health/readiness"]
      interval: 5s
      timeout: 5s
      retries: 30
      start_period: 10s

  agent-executor:
    build:
      context: .
      dockerfile: Dockerfile
      target: production
    restart: unless-stopped

    # Container-level fork-bomb bound: in-jail RLIMIT_NPROC is bypassable
    # via nested user namespaces (fresh per-userns fork counter), so the
    # cgroup pids limit is the reliable bound. 4096 leaves headroom above
    # the in-jail 64-process caps at agent-executor default concurrency (8 x 64 = 512).
    pids_limit: 4096
    networks:
      - core
      - core-db
      - temporal
    # For nsjail, use docker-compose.sandbox.yml with this file.
    # The overlay supplies the required privileges and cgroup bootstrap.
    environment:
      # Common
      LOG_LEVEL: ${LOG_LEVEL}
      TRACECAT__LOG_FORMAT: ${TRACECAT__LOG_FORMAT:-console}
      TRACECAT__PLATFORM_OTEL_ENABLED: ${TRACECAT__PLATFORM_OTEL_ENABLED:-false}
      OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://localhost:4318}
      OTEL_TRACES_SAMPLER: ${OTEL_TRACES_SAMPLER:-parentbased_traceidratio}
      OTEL_TRACES_SAMPLER_ARG: ${OTEL_TRACES_SAMPLER_ARG:-1.0}
      TRACECAT__APP_ENV: production
      TRACECAT__AWS_ASSUME_ROLE_ACCOUNT_ID: ${TRACECAT__AWS_ASSUME_ROLE_ACCOUNT_ID:-}
      TRACECAT__AWS_ASSUME_ROLE_PRINCIPAL_ARN: ${TRACECAT__AWS_ASSUME_ROLE_PRINCIPAL_ARN:-}
      TRACECAT__DB_ENCRYPTION_KEY: ${TRACECAT__DB_ENCRYPTION_KEY} # Sensitive
      TRACECAT__DB_SSLMODE: ${TRACECAT__DB_SSLMODE}
      TRACECAT__DB_URI: ${TRACECAT__DB_URI} # Sensitive
      TRACECAT__SERVICE_KEY: ${TRACECAT__SERVICE_KEY} # Sensitive
      TRACECAT__SERVICE_NAME: agent-executor
      SENTRY_DSN: ${SENTRY_DSN:-}
      TRACECAT__AGENT_SANDBOX_TIMEOUT: ${TRACECAT__AGENT_SANDBOX_TIMEOUT:-10800}
      TRACECAT__DB_POOL_TIMEOUT: ${TRACECAT__DB_POOL_TIMEOUT:-30}
      TRACECAT__DB_AUTH_POOL_SIZE: ${TRACECAT__DB_AUTH_POOL_SIZE:-5}
      TRACECAT__DB_AUTH_MAX_OVERFLOW: ${TRACECAT__DB_AUTH_MAX_OVERFLOW:-5}
      TRACECAT__SIGNING_SECRET: ${TRACECAT__SIGNING_SECRET} # Sensitive
      TRACECAT__FEATURE_FLAGS: ${TRACECAT__FEATURE_FLAGS}
      TRACECAT__EE_MULTI_TENANT: ${TRACECAT__EE_MULTI_TENANT:-false}
      TRACECAT__API_URL: ${TRACECAT__API_URL:-http://api:8000}
      TRACECAT__LITELLM_BASE_URL: ${TRACECAT__LITELLM_BASE_URL:-http://litellm:4000}
      TRACECAT__OUTBOUND_ALLOWED_PRIVATE_CIDRS: ${TRACECAT__OUTBOUND_ALLOWED_PRIVATE_CIDRS:-}
      # Compression (must match worker for codec compatibility)
      TRACECAT__CONTEXT_COMPRESSION_ENABLED: ${TRACECAT__CONTEXT_COMPRESSION_ENABLED:-false}
      TRACECAT__CONTEXT_COMPRESSION_THRESHOLD_KB: ${TRACECAT__CONTEXT_COMPRESSION_THRESHOLD_KB:-16}
      TRACECAT__RESULT_EXTERNALIZATION_ENABLED: ${TRACECAT__RESULT_EXTERNALIZATION_ENABLED:-true}
      TRACECAT__COLLECTION_MANIFESTS_ENABLED: ${TRACECAT__COLLECTION_MANIFESTS_ENABLED:-true}
      TRACECAT__RESULT_EXTERNALIZATION_THRESHOLD_BYTES: ${TRACECAT__RESULT_EXTERNALIZATION_THRESHOLD_BYTES}
      # Registry
      TRACECAT__EXECUTOR_REGISTRY_CACHE_MAX_ENTRIES: ${TRACECAT__EXECUTOR_REGISTRY_CACHE_MAX_ENTRIES:-64}
      TRACECAT__EXECUTOR_REGISTRY_CACHE_MAX_BYTES: ${TRACECAT__EXECUTOR_REGISTRY_CACHE_MAX_BYTES:-10737418240}
      TRACECAT__UNSAFE_DISABLE_SM_MASKING: ${TRACECAT__UNSAFE_DISABLE_SM_MASKING:-false}
      TRACECAT__UNSAFE_DISABLE_SECRET_ERROR_WITHHOLDING: ${TRACECAT__UNSAFE_DISABLE_SECRET_ERROR_WITHHOLDING:-false}
      # Local registry
      TRACECAT__LOCAL_REPOSITORY_PATH: ${TRACECAT__LOCAL_REPOSITORY_PATH}
      TRACECAT__LOCAL_REPOSITORY_ENABLED: ${TRACECAT__LOCAL_REPOSITORY_ENABLED:-false}
      PYTHONPATH: ${PYTHONPATH:+${PYTHONPATH}:}/home/apiuser/.local:/app/local_registry
      # Sandbox
      TRACECAT__EXECUTOR_BACKEND: ${TRACECAT__EXECUTOR_BACKEND:-direct}
      TRACECAT__SANDBOX_NSJAIL_PATH: /usr/local/bin/nsjail
      TRACECAT__SANDBOX_ROOTFS_PATH: /var/lib/tracecat/sandbox-rootfs
      TRACECAT__SANDBOX_CACHE_DIR: /var/lib/tracecat/sandbox-cache
      TRACECAT__SANDBOX_AGENT_ALLOWED_EGRESS_CIDRS: ${TRACECAT__SANDBOX_AGENT_ALLOWED_EGRESS_CIDRS:-}
      TRACECAT__SANDBOX_AGENT_ALLOWED_EGRESS_TCP_PORTS: ${TRACECAT__SANDBOX_AGENT_ALLOWED_EGRESS_TCP_PORTS:-443}
      TRACECAT__SANDBOX_BLOCKED_EGRESS_CIDRS: ${TRACECAT__SANDBOX_BLOCKED_EGRESS_CIDRS:-}
      TRACECAT__SANDBOX_DNS_ALLOWED_EGRESS_CIDRS: ${TRACECAT__SANDBOX_DNS_ALLOWED_EGRESS_CIDRS:-}
      TRACECAT__SANDBOX_ALLOW_PUBLIC_IPV6_EGRESS: ${TRACECAT__SANDBOX_ALLOW_PUBLIC_IPV6_EGRESS:-false}
      # Temporal
      TEMPORAL__CLUSTER_URL: ${TEMPORAL__CLUSTER_URL}
      TEMPORAL__CLUSTER_NAMESPACE: ${TEMPORAL__CLUSTER_NAMESPACE}
      TEMPORAL__API_KEY: ${TEMPORAL__API_KEY}
      TEMPORAL__PAYLOAD_ENCRYPTION_ENABLED: ${TEMPORAL__PAYLOAD_ENCRYPTION_ENABLED:-false}
      TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING: ${TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING:-}
      TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING_ARN: ${TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING_ARN:-}
      TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_TTL_SECONDS: ${TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_TTL_SECONDS:-3600}
      TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_MAX_ITEMS: ${TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_MAX_ITEMS:-128}
      # Agent worker configuration
      TRACECAT__AGENT_QUEUE: ${TRACECAT__AGENT_QUEUE:-shared-agent-queue}
      TRACECAT__AGENT_EXECUTOR_QUEUE: ${TRACECAT__AGENT_EXECUTOR_QUEUE:-shared-agent-executor-queue}
      TRACECAT__EXECUTOR_QUEUE: ${TRACECAT__EXECUTOR_QUEUE:-shared-action-queue}
      TRACECAT__AGENT_EXECUTOR_MAX_CONCURRENT_ACTIVITIES: ${TRACECAT__AGENT_EXECUTOR_MAX_CONCURRENT_ACTIVITIES:-8}
      TRACECAT__AGENT_SANDBOX_MEMORY_MB: ${TRACECAT__AGENT_SANDBOX_MEMORY_MB:-4096}
      TRACECAT__AGENT_SANDBOX_ADDRESS_SPACE_MB: ${TRACECAT__AGENT_SANDBOX_ADDRESS_SPACE_MB:-}
      # Keep the thread pool aligned with agent activity concurrency.
      TEMPORAL__THREADPOOL_MAX_WORKERS: ${TRACECAT__AGENT_EXECUTOR_MAX_CONCURRENT_ACTIVITIES:-8}
      TRACECAT__LLM_PROXY_READ_TIMEOUT: ${TRACECAT__LLM_PROXY_READ_TIMEOUT:-600}
      TRACECAT__LLM_GATEWAY_CREDENTIAL_CACHE_TTL_SECONDS: ${TRACECAT__LLM_GATEWAY_CREDENTIAL_CACHE_TTL_SECONDS:-60}
      TRACECAT__LLM_GATEWAY_HEALTHCHECK_INTERVAL_SECONDS: ${TRACECAT__LLM_GATEWAY_HEALTHCHECK_INTERVAL_SECONDS:-30}
      TRACECAT__LLM_GATEWAY_HEALTHCHECK_TIMEOUT_SECONDS: ${TRACECAT__LLM_GATEWAY_HEALTHCHECK_TIMEOUT_SECONDS:-2}
      TRACECAT__LLM_GATEWAY_CONNECT_TIMEOUT_SECONDS: ${TRACECAT__LLM_GATEWAY_CONNECT_TIMEOUT_SECONDS:-}
      TRACECAT__LLM_GATEWAY_READ_TIMEOUT_SECONDS: ${TRACECAT__LLM_GATEWAY_READ_TIMEOUT_SECONDS:-}
      TRACECAT__LLM_GATEWAY_WRITE_TIMEOUT_SECONDS: ${TRACECAT__LLM_GATEWAY_WRITE_TIMEOUT_SECONDS:-}
      TRACECAT__LLM_GATEWAY_POOL_TIMEOUT_SECONDS: ${TRACECAT__LLM_GATEWAY_POOL_TIMEOUT_SECONDS:-}
      TRACECAT__LLM_GATEWAY_FAILURE_THRESHOLD: ${TRACECAT__LLM_GATEWAY_FAILURE_THRESHOLD:-3}
      TRACECAT__LLM_GATEWAY_STATUS_LOG_INTERVAL_SECONDS: ${TRACECAT__LLM_GATEWAY_STATUS_LOG_INTERVAL_SECONDS:-30}
      # Redis
      REDIS_URL: ${REDIS_URL}
      # Blob storage (for S3/MinIO tarball access)
      TRACECAT__BLOB_STORAGE_ENDPOINT: ${TRACECAT__BLOB_STORAGE_ENDPOINT:-http://minio:9000}
      TRACECAT__BLOB_STORAGE_SSL_VERIFY: ${TRACECAT__BLOB_STORAGE_SSL_VERIFY:-true}
      TRACECAT__BLOB_STORAGE_BUCKET_SKILLS: ${TRACECAT__BLOB_STORAGE_BUCKET_SKILLS:-tracecat-skills}
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
    volumes:
      - ${TRACECAT__LOCAL_REPOSITORY_PATH}:/app/local_registry
      - sandbox-cache:/var/lib/tracecat/sandbox-cache
    command: ["python", "-m", "tracecat.agent.executor_worker"]
    depends_on:
      litellm:
        condition: service_healthy
      migrations:
        condition: service_completed_successfully
      temporal:
        condition: service_healthy
      redis:
        condition: service_healthy
      minio:
        condition: service_healthy

  mcp:
    build:
      context: .
      dockerfile: Dockerfile
      target: production
    restart: on-failure:3
    networks:
      - core
      - core-db
      - temporal
    environment:
      TRACECAT__AUDIT_TRUSTED_PROXY_CIDRS: ${TRACECAT__AUDIT_TRUSTED_PROXY_CIDRS:-}
      LOG_LEVEL: ${LOG_LEVEL}
      TRACECAT__LOG_FORMAT: ${TRACECAT__LOG_FORMAT:-console}
      TRACECAT__APP_ENV: production
      TRACECAT__DB_ENCRYPTION_KEY: ${TRACECAT__DB_ENCRYPTION_KEY}
      TRACECAT__DB_SSLMODE: ${TRACECAT__DB_SSLMODE}
      TRACECAT__DB_URI: ${TRACECAT__DB_URI}
      TRACECAT__SERVICE_KEY: ${TRACECAT__SERVICE_KEY}
      TRACECAT__SERVICE_NAME: mcp
      TRACECAT__AGENT_SANDBOX_TIMEOUT: ${TRACECAT__AGENT_SANDBOX_TIMEOUT:-10800}
      TRACECAT__DB_POOL_TIMEOUT: ${TRACECAT__DB_POOL_TIMEOUT:-30}
      TRACECAT__DB_AUTH_POOL_SIZE: ${TRACECAT__DB_AUTH_POOL_SIZE:-5}
      TRACECAT__DB_AUTH_MAX_OVERFLOW: ${TRACECAT__DB_AUTH_MAX_OVERFLOW:-5}
      TRACECAT__SIGNING_SECRET: ${TRACECAT__SIGNING_SECRET}
      TRACECAT__FEATURE_FLAGS: ${TRACECAT__FEATURE_FLAGS}
      TRACECAT__EE_MULTI_TENANT: ${TRACECAT__EE_MULTI_TENANT:-false}
      REDIS_URL: ${REDIS_URL}
      # Internal OIDC issuer (on the API server)
      USER_AUTH_SECRET: ${USER_AUTH_SECRET}
      TRACECAT__API_URL: ${TRACECAT__API_URL:-http://api:8000}
      TRACECAT__PUBLIC_APP_URL: ${TRACECAT__PUBLIC_APP_URL}
      TRACECAT__PUBLIC_API_URL: ${TRACECAT__PUBLIC_API_URL:-http://localhost:${PUBLIC_APP_PORT:-80}/api}
      TRACECAT_MCP__FILE_TRANSFER_URL_EXPIRY_SECONDS: ${TRACECAT_MCP__FILE_TRANSFER_URL_EXPIRY_SECONDS:-300}
      TRACECAT_MCP__STARTUP_MAX_ATTEMPTS: ${TRACECAT_MCP__STARTUP_MAX_ATTEMPTS:-3}
      TRACECAT_MCP__STARTUP_RETRY_DELAY_SECONDS: ${TRACECAT_MCP__STARTUP_RETRY_DELAY_SECONDS:-2}
      TEMPORAL__CLUSTER_URL: ${TEMPORAL__CLUSTER_URL}
      TRACECAT__BACKGROUND_QUEUE: ${TRACECAT__BACKGROUND_QUEUE:-shared-background-queue}
      TEMPORAL__CLUSTER_QUEUE: ${TEMPORAL__CLUSTER_QUEUE}
      TEMPORAL__CLUSTER_NAMESPACE: ${TEMPORAL__CLUSTER_NAMESPACE}
      TEMPORAL__API_KEY: ${TEMPORAL__API_KEY}
      TEMPORAL__PAYLOAD_ENCRYPTION_ENABLED: ${TEMPORAL__PAYLOAD_ENCRYPTION_ENABLED:-false}
      TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING: ${TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING:-}
      TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING_ARN: ${TEMPORAL__PAYLOAD_ENCRYPTION_KEYRING_ARN:-}
      TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_TTL_SECONDS: ${TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_TTL_SECONDS:-3600}
      TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_MAX_ITEMS: ${TEMPORAL__PAYLOAD_ENCRYPTION_CACHE_MAX_ITEMS:-128}
      # Blob Storage
      TRACECAT__BLOB_STORAGE_ENDPOINT: ${TRACECAT__BLOB_STORAGE_ENDPOINT:-http://minio:9000}
      TRACECAT__BLOB_STORAGE_SSL_VERIFY: ${TRACECAT__BLOB_STORAGE_SSL_VERIFY:-true}
      TRACECAT__BLOB_STORAGE_BUCKET_SKILLS: ${TRACECAT__BLOB_STORAGE_BUCKET_SKILLS:-tracecat-skills}
      TRACECAT__BLOB_STORAGE_PRESIGNED_URL_ENDPOINT: ${TRACECAT__BLOB_STORAGE_PRESIGNED_URL_ENDPOINT:-${TRACECAT__PUBLIC_APP_URL}/s3}
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
    command: ["python", "-m", "tracecat.mcp"]
    depends_on:
      api:
        condition: service_healthy
      migrations:
        condition: service_completed_successfully
      temporal:
        condition: service_healthy
      minio:
        condition: service_healthy
      redis:
        condition: service_healthy

  ui:
    build:
      context: ./frontend
      dockerfile: Dockerfile.prod
    container_name: ui
    restart: unless-stopped
    networks:
      - core
    environment:
      NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
      NEXT_PUBLIC_APP_ENV: production
      NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL}
      NEXT_PUBLIC_AUTH_TYPES: ${TRACECAT__AUTH_TYPES}
      NEXT_SERVER_API_URL: ${NEXT_SERVER_API_URL}
      NODE_ENV: production
      TRACECAT__CSP_CONNECT_SRC_ORIGINS: ${TRACECAT__CSP_CONNECT_SRC_ORIGINS:-${TRACECAT__BLOB_STORAGE_PRESIGNED_URL_ENDPOINT:-${TRACECAT__PUBLIC_APP_URL}/s3}}
      TRACECAT__SERVICE_KEY: ${TRACECAT__SERVICE_KEY}
    depends_on:
      - api

  postgres_db:
    image: ${TRACECAT__PGVECTOR_IMAGE:-ghcr.io/tracecathq/tracecat-postgres:1}
    container_name: postgres_db
    command: ["postgres"]
    restart: unless-stopped
    networks:
      - core-db
    ports:
      - 5432:5432
    shm_size: 128mb
    environment:
      POSTGRES_DB: postgres
      POSTGRES_USER: ${TRACECAT__POSTGRES_USER}
      POSTGRES_PASSWORD: ${TRACECAT__POSTGRES_PASSWORD}
    volumes:
      - core-db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U $${POSTGRES_USER} -d postgres"]
      interval: 5s
      timeout: 5s
      retries: 12
      start_period: 180s

  migrations:
    build:
      context: .
      dockerfile: Dockerfile
      target: production
    container_name: migrations
    restart: "no"
    networks:
      - core-db
    environment:
      LOG_LEVEL: ${LOG_LEVEL}
      TRACECAT__LOG_FORMAT: ${TRACECAT__LOG_FORMAT:-console}
      # The app's database: role setup checks it names the admin URI's database.
      TRACECAT__DB_URI: ${TRACECAT__DB_URI} # Sensitive
      # Migrations log in as migrator once database role setup is on.
      TRACECAT__DB_MIGRATOR_URI: ${TRACECAT__DB_MIGRATOR_URI:-} # Sensitive
      # With TRACECAT__DB_ROLE_LOGIN set, role setup runs as TRACECAT__DB_ADMIN_URI first.
      TRACECAT__DB_ADMIN_URI: ${TRACECAT__DB_ADMIN_URI:-} # Sensitive
      TRACECAT__DB_MIGRATOR_PASSWORD: ${TRACECAT__DB_MIGRATOR_PASSWORD:-} # Sensitive
      TRACECAT__DB_APP_PASSWORD: ${TRACECAT__DB_APP_PASSWORD:-} # Sensitive
      TRACECAT__DB_ROLE_LOGIN: ${TRACECAT__DB_ROLE_LOGIN:-}
      TRACECAT__DB_SSLMODE: ${TRACECAT__DB_SSLMODE}
      TRACECAT__FEATURE_FLAGS: ${TRACECAT__FEATURE_FLAGS}
    # Role setup (when on) as the admin, then migrations as migrator.
    command:
      - sh
      - -ec
      - |
        if [ -n "$${TRACECAT__DB_ROLE_LOGIN}" ]; then
          python3 -m tracecat.db.roles upgrade --login "$${TRACECAT__DB_ROLE_LOGIN}"
        fi
        # The migrations get migrator's login only, never the admin's.
        (
          unset TRACECAT__DB_ADMIN_URI TRACECAT__DB_MIGRATOR_PASSWORD TRACECAT__DB_APP_PASSWORD
          if [ -n "$${TRACECAT__DB_MIGRATOR_URI}" ]; then
            export TRACECAT__DB_URI="$${TRACECAT__DB_MIGRATOR_URI}"
          fi
          exec python3 -m tracecat.db.roles migrate
        )
        # Again once the schema exists: installs the dynamic-table RLS guard on a new install.
        if [ -n "$${TRACECAT__DB_ROLE_LOGIN}" ]; then
          python3 -m tracecat.db.roles upgrade --login "$${TRACECAT__DB_ROLE_LOGIN}"
        fi
    depends_on:
      postgres_db:
        condition: service_healthy

  temporal_postgres_db:
    image: postgres:13
    container_name: temporal_postgres_db
    restart: unless-stopped
    networks:
      - temporal-db
    environment:
      POSTGRES_USER: ${TEMPORAL__POSTGRES_USER}
      POSTGRES_PASSWORD: ${TEMPORAL__POSTGRES_PASSWORD} # Sensitive
    volumes:
      - temporal-db:/var/lib/postgresql/data

  temporal:
    image: temporalio/auto-setup:${TEMPORAL__VERSION:-1.27.1}
    container_name: temporal
    restart: unless-stopped
    ports:
      - 7233:7233
    networks:
      - core
      - temporal
      - temporal-db
    environment:
      - DB=postgres12
      - DB_PORT=5432
      - POSTGRES_USER=${TEMPORAL__POSTGRES_USER}
      - POSTGRES_PWD=${TEMPORAL__POSTGRES_PASSWORD} # Sensitive
      - POSTGRES_SEEDS=temporal_postgres_db
      - DEFAULT_NAMESPACE_RETENTION=${TEMPORAL__DEFAULT_NAMESPACE_RETENTION:-24h}
      - LOG_LEVEL=warn
      - BIND_ON_IP=0.0.0.0
      - TEMPORAL_BROADCAST_ADDRESS=127.0.0.1
    healthcheck:
      test: ["CMD", "tctl", "--address", "temporal:7233", "cluster", "health"]
      interval: 5s
      timeout: 10s
      retries: 30
    depends_on:
      - temporal_postgres_db

  temporal_ui:
    image: temporalio/ui:${TEMPORAL__UI_VERSION:-latest}
    container_name: temporal_ui
    restart: unless-stopped
    networks:
      - temporal
      - core
    ports:
      - ${TEMPORAL_UI_PORT:-8081}:8081
    environment:
      - TEMPORAL_ADDRESS=temporal:7233
      - TEMPORAL_CORS_ORIGINS=http://localhost:${TEMPORAL_UI_PORT:-8081}
    depends_on:
      - temporal

  minio-init:
    image: cgr.dev/chainguard/minio:latest@sha256:6a1d0b45c8669726bba580ced0bfa4cb9fdeed1ed636dfabd81d1577beb6937b
    user: "0:0"
    entrypoint: ["chown", "-R", "65532:65532", "/data"]
    volumes:
      - minio-data:/data
    network_mode: none
    restart: "no"

  minio:
    image: cgr.dev/chainguard/minio:latest@sha256:6a1d0b45c8669726bba580ced0bfa4cb9fdeed1ed636dfabd81d1577beb6937b
    container_name: minio
    restart: unless-stopped
    networks:
      - core
    ports:
      - ${MINIO_PORT:-9000}:9000
      - ${MINIO_CONSOLE_PORT:-9001}:9001
    environment:
      MINIO_ROOT_USER: ${MINIO_ROOT_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
    volumes:
      - minio-data:/data
    command: server /data --console-address ":9001"
    depends_on:
      minio-init:
        condition: service_completed_successfully
    healthcheck:
      test: ["CMD", "mc", "ready", "local"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    container_name: redis
    restart: unless-stopped
    networks:
      - core
    ports:
      - 6379:6379
    volumes:
      - redis-data:/data
    command: redis-server --appendonly yes
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 5s
      retries: 5

volumes:
  core-db:
  temporal-db:
  minio-data:
  redis-data:
  sandbox-cache:

networks:
  core:
  core-db:
    # Not internal - allows host access for integration tests
  temporal:
  temporal-db:
    internal: true