services: postgres: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB:-crank} POSTGRES_USER: ${POSTGRES_USER:-crank} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-crank} volumes: - postgres_data:/var/lib/postgresql/data ports: - "${POSTGRES_PUBLISH_BIND:-127.0.0.1}:${POSTGRES_PUBLISH_PORT:-5432}:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-crank} -d ${POSTGRES_DB:-crank}"] interval: 10s timeout: 5s retries: 5 valkey: image: valkey/valkey:8-alpine restart: unless-stopped profiles: - cache command: ["valkey-server", "--save", "", "--appendonly", "no"] volumes: - valkey_data:/data ports: - "127.0.0.1:6379:6379" healthcheck: test: ["CMD", "valkey-cli", "ping"] interval: 10s timeout: 5s retries: 5 migrate: image: ${CRANK_ADMIN_API_IMAGE:-crank/admin-api:dev} build: context: ../.. dockerfile: apps/admin-api/Dockerfile command: ["crank-migrate", "apply"] restart: "no" depends_on: postgres: condition: service_healthy environment: POSTGRES_HOST: ${POSTGRES_HOST:-postgres} POSTGRES_PORT: ${POSTGRES_PORT:-5432} POSTGRES_DB: ${POSTGRES_DB:-crank} POSTGRES_USER: ${POSTGRES_USER:-crank} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-crank} CRANK_DATABASE_URL: ${CRANK_DATABASE_URL:-} POSTGRES_MAX_CONNECTIONS: ${POSTGRES_MAX_CONNECTIONS:-20} POSTGRES_MIN_CONNECTIONS: ${POSTGRES_MIN_CONNECTIONS:-2} POSTGRES_ACQUIRE_TIMEOUT_MS: ${POSTGRES_ACQUIRE_TIMEOUT_MS:-5000} POSTGRES_IDLE_TIMEOUT_MS: ${POSTGRES_IDLE_TIMEOUT_MS:-600000} POSTGRES_MAX_LIFETIME_MS: ${POSTGRES_MAX_LIFETIME_MS:-1800000} artifact-storage-init: image: ${CRANK_ADMIN_API_IMAGE:-crank/admin-api:dev} build: context: ../.. dockerfile: apps/admin-api/Dockerfile entrypoint: ["/bin/sh", "-ec"] command: ["install -d -m 700 -- \"$${CRANK_STORAGE_ROOT}\""] restart: "no" environment: CRANK_STORAGE_ROOT: ${CRANK_STORAGE_ROOT:-/var/lib/crank/storage} volumes: - artifact_storage:${CRANK_STORAGE_ROOT:-/var/lib/crank/storage} admin-api: image: ${CRANK_ADMIN_API_IMAGE:-crank/admin-api:dev} build: context: ../.. dockerfile: apps/admin-api/Dockerfile restart: unless-stopped depends_on: migrate: condition: service_completed_successfully artifact-storage-init: condition: service_completed_successfully environment: POSTGRES_HOST: ${POSTGRES_HOST:-postgres} POSTGRES_PORT: ${POSTGRES_PORT:-5432} POSTGRES_DB: ${POSTGRES_DB:-crank} POSTGRES_USER: ${POSTGRES_USER:-crank} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-crank} CRANK_DATABASE_URL: ${CRANK_DATABASE_URL:-} POSTGRES_MAX_CONNECTIONS: ${POSTGRES_MAX_CONNECTIONS:-20} POSTGRES_MIN_CONNECTIONS: ${POSTGRES_MIN_CONNECTIONS:-2} POSTGRES_ACQUIRE_TIMEOUT_MS: ${POSTGRES_ACQUIRE_TIMEOUT_MS:-5000} POSTGRES_IDLE_TIMEOUT_MS: ${POSTGRES_IDLE_TIMEOUT_MS:-600000} POSTGRES_MAX_LIFETIME_MS: ${POSTGRES_MAX_LIFETIME_MS:-1800000} CRANK_STORAGE_ROOT: ${CRANK_STORAGE_ROOT:-/var/lib/crank/storage} CRANK_ADMIN_BIND: ${CRANK_ADMIN_BIND:-0.0.0.0:3001} CRANK_CACHE_BACKEND: ${CRANK_CACHE_BACKEND:-memory} CRANK_CACHE_URL: ${CRANK_CACHE_URL:-} CRANK_ADMIN_RATE_LIMIT_RPS: ${CRANK_ADMIN_RATE_LIMIT_RPS:-30} CRANK_ADMIN_RATE_LIMIT_BURST: ${CRANK_ADMIN_RATE_LIMIT_BURST:-60} CRANK_RUNTIME_MAX_CONCURRENT_UNARY: ${CRANK_RUNTIME_MAX_CONCURRENT_UNARY:-64} CRANK_ENVIRONMENT: ${CRANK_ENVIRONMENT:-production} CRANK_LOG_LEVEL: ${CRANK_LOG_LEVEL:-} CRANK_SENTRY_DSN: ${CRANK_SENTRY_DSN:-} CRANK_METRICS_ENABLED: ${CRANK_METRICS_ENABLED:-true} CRANK_ADMIN_METRICS_BIND: ${CRANK_ADMIN_METRICS_BIND:-127.0.0.1:9464} CRANK_METRICS_BEARER_TOKEN: ${CRANK_METRICS_BEARER_TOKEN:-} CRANK_INVOCATION_LOG_RETENTION_DAYS: ${CRANK_INVOCATION_LOG_RETENTION_DAYS:-30} OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-} OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: ${OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:-} OTEL_EXPORTER_OTLP_PROTOCOL: ${OTEL_EXPORTER_OTLP_PROTOCOL:-http/protobuf} OTEL_EXPORTER_OTLP_TRACES_PROTOCOL: ${OTEL_EXPORTER_OTLP_TRACES_PROTOCOL:-} OTEL_EXPORTER_OTLP_TIMEOUT: ${OTEL_EXPORTER_OTLP_TIMEOUT:-10000} OTEL_EXPORTER_OTLP_TRACES_TIMEOUT: ${OTEL_EXPORTER_OTLP_TRACES_TIMEOUT:-} OTEL_EXPORTER_OTLP_HEADERS: ${OTEL_EXPORTER_OTLP_HEADERS:-} OTEL_EXPORTER_OTLP_TRACES_HEADERS: ${OTEL_EXPORTER_OTLP_TRACES_HEADERS:-} OTEL_BSP_MAX_QUEUE_SIZE: ${OTEL_BSP_MAX_QUEUE_SIZE:-2048} OTEL_BSP_MAX_EXPORT_BATCH_SIZE: ${OTEL_BSP_MAX_EXPORT_BATCH_SIZE:-512} OTEL_BSP_SCHEDULE_DELAY: ${OTEL_BSP_SCHEDULE_DELAY:-5000} OTEL_BSP_EXPORT_TIMEOUT: ${OTEL_BSP_EXPORT_TIMEOUT:-30000} CRANK_MASTER_KEY: ${CRANK_MASTER_KEY} CRANK_BASE_URL: ${CRANK_BASE_URL:-http://localhost:3000} CRANK_SESSION_SECRET: ${CRANK_SESSION_SECRET} CRANK_PASSWORD_PEPPER: ${CRANK_PASSWORD_PEPPER} CRANK_SESSION_TTL_HOURS: ${CRANK_SESSION_TTL_HOURS:-24} CRANK_TRUSTED_PROXY_IPS: ${CRANK_TRUSTED_PROXY_IPS:-} CRANK_BOOTSTRAP_ADMIN_EMAIL: ${CRANK_BOOTSTRAP_ADMIN_EMAIL} CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME: ${CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME:-Crank Owner} CRANK_DEMO_SEED: ${CRANK_DEMO_SEED:-false} CRANK_OUTBOUND_ALLOWED_HOSTS: ${CRANK_OUTBOUND_ALLOWED_HOSTS:-} CRANK_OUTBOUND_DENIED_HOSTS: ${CRANK_OUTBOUND_DENIED_HOSTS:-} CRANK_OUTBOUND_MAX_REQUEST_BYTES: ${CRANK_OUTBOUND_MAX_REQUEST_BYTES:-4194304} CRANK_OUTBOUND_MAX_RESPONSE_BYTES: ${CRANK_OUTBOUND_MAX_RESPONSE_BYTES:-4194304} CRANK_IMPORT_EXTERNAL_REFERENCE_ALLOWED_URL_PREFIXES: ${CRANK_IMPORT_EXTERNAL_REFERENCE_ALLOWED_URL_PREFIXES:-} CRANK_IMPORT_EXTERNAL_REFERENCE_MAX_DEPTH: ${CRANK_IMPORT_EXTERNAL_REFERENCE_MAX_DEPTH:-8} CRANK_IMPORT_EXTERNAL_REFERENCE_MAX_DOCUMENTS: ${CRANK_IMPORT_EXTERNAL_REFERENCE_MAX_DOCUMENTS:-32} CRANK_IMPORT_EXTERNAL_REFERENCE_MAX_FETCH_BYTES: ${CRANK_IMPORT_EXTERNAL_REFERENCE_MAX_FETCH_BYTES:-262144} CRANK_IMPORT_EXTERNAL_REFERENCE_FETCH_TIMEOUT_MS: ${CRANK_IMPORT_EXTERNAL_REFERENCE_FETCH_TIMEOUT_MS:-10000} CRANK_IMPORT_EXTERNAL_REFERENCE_MAX_EXPANDED_NODES: ${CRANK_IMPORT_EXTERNAL_REFERENCE_MAX_EXPANDED_NODES:-10000} volumes: - artifact_storage:${CRANK_STORAGE_ROOT:-/var/lib/crank/storage} ports: - "${CRANK_PUBLISH_BIND:-127.0.0.1}:${CRANK_ADMIN_PUBLISH_PORT:-3001}:3001" healthcheck: test: ["CMD", "/usr/local/bin/crank-http-healthcheck", "3001", "/ready"] interval: 15s timeout: 5s retries: 5 mcp-server: image: ${CRANK_MCP_SERVER_IMAGE:-crank/mcp-server:dev} build: context: ../.. dockerfile: apps/mcp-server/Dockerfile restart: unless-stopped depends_on: migrate: condition: service_completed_successfully environment: POSTGRES_HOST: ${POSTGRES_HOST:-postgres} POSTGRES_PORT: ${POSTGRES_PORT:-5432} POSTGRES_DB: ${POSTGRES_DB:-crank} POSTGRES_USER: ${POSTGRES_USER:-crank} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-crank} CRANK_DATABASE_URL: ${CRANK_DATABASE_URL:-} POSTGRES_MAX_CONNECTIONS: ${POSTGRES_MAX_CONNECTIONS:-20} POSTGRES_MIN_CONNECTIONS: ${POSTGRES_MIN_CONNECTIONS:-2} POSTGRES_ACQUIRE_TIMEOUT_MS: ${POSTGRES_ACQUIRE_TIMEOUT_MS:-5000} POSTGRES_IDLE_TIMEOUT_MS: ${POSTGRES_IDLE_TIMEOUT_MS:-600000} POSTGRES_MAX_LIFETIME_MS: ${POSTGRES_MAX_LIFETIME_MS:-1800000} CRANK_MCP_BIND: ${CRANK_MCP_BIND:-0.0.0.0:3002} CRANK_MCP_REFRESH_MS: ${CRANK_MCP_REFRESH_MS:-5000} CRANK_CACHE_BACKEND: ${CRANK_CACHE_BACKEND:-memory} CRANK_CACHE_URL: ${CRANK_CACHE_URL:-} CRANK_MCP_RATE_LIMIT_RPS: ${CRANK_MCP_RATE_LIMIT_RPS:-60} CRANK_MCP_RATE_LIMIT_BURST: ${CRANK_MCP_RATE_LIMIT_BURST:-120} CRANK_RUNTIME_MAX_CONCURRENT_UNARY: ${CRANK_RUNTIME_MAX_CONCURRENT_UNARY:-64} CRANK_RUNTIME_MAX_CONCURRENT_SESSIONS: ${CRANK_RUNTIME_MAX_CONCURRENT_SESSIONS:-16} CRANK_ENVIRONMENT: ${CRANK_ENVIRONMENT:-production} CRANK_LOG_LEVEL: ${CRANK_LOG_LEVEL:-} CRANK_SENTRY_DSN: ${CRANK_SENTRY_DSN:-} CRANK_METRICS_ENABLED: ${CRANK_METRICS_ENABLED:-true} CRANK_MCP_METRICS_BIND: ${CRANK_MCP_METRICS_BIND:-127.0.0.1:9465} CRANK_METRICS_BEARER_TOKEN: ${CRANK_METRICS_BEARER_TOKEN:-} OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-} OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: ${OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:-} OTEL_EXPORTER_OTLP_PROTOCOL: ${OTEL_EXPORTER_OTLP_PROTOCOL:-http/protobuf} OTEL_EXPORTER_OTLP_TRACES_PROTOCOL: ${OTEL_EXPORTER_OTLP_TRACES_PROTOCOL:-} OTEL_EXPORTER_OTLP_TIMEOUT: ${OTEL_EXPORTER_OTLP_TIMEOUT:-10000} OTEL_EXPORTER_OTLP_TRACES_TIMEOUT: ${OTEL_EXPORTER_OTLP_TRACES_TIMEOUT:-} OTEL_EXPORTER_OTLP_HEADERS: ${OTEL_EXPORTER_OTLP_HEADERS:-} OTEL_EXPORTER_OTLP_TRACES_HEADERS: ${OTEL_EXPORTER_OTLP_TRACES_HEADERS:-} OTEL_BSP_MAX_QUEUE_SIZE: ${OTEL_BSP_MAX_QUEUE_SIZE:-2048} OTEL_BSP_MAX_EXPORT_BATCH_SIZE: ${OTEL_BSP_MAX_EXPORT_BATCH_SIZE:-512} OTEL_BSP_SCHEDULE_DELAY: ${OTEL_BSP_SCHEDULE_DELAY:-5000} OTEL_BSP_EXPORT_TIMEOUT: ${OTEL_BSP_EXPORT_TIMEOUT:-30000} CRANK_MASTER_KEY: ${CRANK_MASTER_KEY} CRANK_BASE_URL: ${CRANK_BASE_URL:-http://localhost:3000} CRANK_OUTBOUND_ALLOWED_HOSTS: ${CRANK_OUTBOUND_ALLOWED_HOSTS:-} CRANK_OUTBOUND_DENIED_HOSTS: ${CRANK_OUTBOUND_DENIED_HOSTS:-} CRANK_OUTBOUND_MAX_REQUEST_BYTES: ${CRANK_OUTBOUND_MAX_REQUEST_BYTES:-4194304} CRANK_OUTBOUND_MAX_RESPONSE_BYTES: ${CRANK_OUTBOUND_MAX_RESPONSE_BYTES:-4194304} ports: - "${CRANK_PUBLISH_BIND:-127.0.0.1}:${CRANK_MCP_PUBLISH_PORT:-3002}:3002" healthcheck: test: ["CMD", "/usr/local/bin/crank-http-healthcheck", "3002", "/ready"] interval: 15s timeout: 5s retries: 5 ui: image: ${CRANK_UI_IMAGE:-crank/ui:dev} build: context: ../.. dockerfile: apps/ui/Dockerfile restart: unless-stopped depends_on: admin-api: condition: service_healthy mcp-server: condition: service_healthy ports: - "${CRANK_PUBLISH_BIND:-127.0.0.1}:${CRANK_UI_PUBLISH_PORT:-3000}:3000" healthcheck: test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/"] interval: 15s timeout: 5s retries: 5 volumes: postgres_data: artifact_storage: valkey_data: