name: skillgo services: db: image: postgres:16-alpine environment: POSTGRES_DB: ${POSTGRES_DB:-skillgo} POSTGRES_USER: ${POSTGRES_USER:-skillgo} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD} volumes: - skillgo-postgres:/var/lib/postgresql/data networks: - backend healthcheck: test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] interval: 5s timeout: 3s retries: 20 restart: unless-stopped mem_limit: 512m cpus: 0.5 api: build: context: ./backend environment: SKILLGO_ENVIRONMENT: production SKILLGO_ENVIRONMENT_PREPARATION_ENABLED: ${SKILLGO_ENVIRONMENT_PREPARATION_ENABLED:-false} SKILLGO_ENVIRONMENT_BASE_IMAGE: ${SKILLGO_ENVIRONMENT_BASE_IMAGE:-} SKILLGO_ENVIRONMENT_QUEUE_WAIT_SECONDS: ${SKILLGO_ENVIRONMENT_QUEUE_WAIT_SECONDS:-3600} SKILLGO_DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-skillgo}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-skillgo} SKILLGO_STORAGE_ROOT: /var/lib/skillgo/storage SKILLGO_DURABLE_CHECKPOINTS_ENABLED: ${SKILLGO_DURABLE_CHECKPOINTS_ENABLED:-false} SKILLGO_JWT_SECRET: ${SKILLGO_JWT_SECRET:?set SKILLGO_JWT_SECRET} SKILLGO_BOOTSTRAP_EMAIL: ${SKILLGO_BOOTSTRAP_EMAIL:?set SKILLGO_BOOTSTRAP_EMAIL} SKILLGO_BOOTSTRAP_PASSWORD: ${SKILLGO_BOOTSTRAP_PASSWORD:?set SKILLGO_BOOTSTRAP_PASSWORD} SKILLGO_BOOTSTRAP_NAME: ${SKILLGO_BOOTSTRAP_NAME:-SkillGo Owner} SKILLGO_MODEL_BASE_URL: ${SKILLGO_MODEL_BASE_URL:-} SKILLGO_MODEL_API_KEY: ${SKILLGO_MODEL_API_KEY:-} SKILLGO_MODEL_NAME: ${SKILLGO_MODEL_NAME:-} SKILLGO_MODEL_TIMEOUT_SECONDS: ${SKILLGO_MODEL_TIMEOUT_SECONDS:-0} SKILLGO_MODEL_CONNECT_TIMEOUT_SECONDS: ${SKILLGO_MODEL_CONNECT_TIMEOUT_SECONDS:-15} SKILLGO_MODEL_FIRST_CHUNK_TIMEOUT_SECONDS: ${SKILLGO_MODEL_FIRST_CHUNK_TIMEOUT_SECONDS:-600} SKILLGO_MODEL_STREAM_STALL_TIMEOUT_SECONDS: ${SKILLGO_MODEL_STREAM_STALL_TIMEOUT_SECONDS:-600} SKILLGO_SKILL_ANALYSIS_TIMEOUT_SECONDS: ${SKILLGO_SKILL_ANALYSIS_TIMEOUT_SECONDS:-45} SKILLGO_CONVERSATION_TURN_STALE_SECONDS: ${SKILLGO_CONVERSATION_TURN_STALE_SECONDS:-3600} SKILLGO_MODEL_TEMPERATURE: ${SKILLGO_MODEL_TEMPERATURE:-0.2} SKILLGO_MODEL_JSON_MODE: ${SKILLGO_MODEL_JSON_MODE:-true} SKILLGO_MODEL_NATIVE_TOOLS: ${SKILLGO_MODEL_NATIVE_TOOLS:-true} SKILLGO_MODEL_TLS_VERIFY: ${SKILLGO_MODEL_TLS_VERIFY:-true} SKILLGO_SANDBOX_WORKER_ENABLED: ${SKILLGO_SANDBOX_WORKER_ENABLED:-false} SKILLGO_PLATFORM_DOCUMENT_TOOLS_ENABLED: ${SKILLGO_PLATFORM_DOCUMENT_TOOLS_ENABLED:-false} SKILLGO_ARTIFACT_DELIVERY_ONLY: ${SKILLGO_ARTIFACT_DELIVERY_ONLY:-true} SKILLGO_CONTEXT_OBSERVABILITY: ${SKILLGO_CONTEXT_OBSERVABILITY:-false} SKILLGO_WORKFLOW_AUTO_DELIVERY_VERSION_IDS: ${SKILLGO_WORKFLOW_AUTO_DELIVERY_VERSION_IDS:-} SKILLGO_DELIVERY_ONLY_SKILL_IDS: ${SKILLGO_DELIVERY_ONLY_SKILL_IDS:-} SKILLGO_AGENT_RUN_SUCCESS_DETAIL_DAYS: ${SKILLGO_AGENT_RUN_SUCCESS_DETAIL_DAYS:-7} SKILLGO_AGENT_RUN_FAILURE_DETAIL_DAYS: ${SKILLGO_AGENT_RUN_FAILURE_DETAIL_DAYS:-30} SKILLGO_AGENT_RUN_CLEANUP_INTERVAL_SECONDS: ${SKILLGO_AGENT_RUN_CLEANUP_INTERVAL_SECONDS:-3600} SKILLGO_STORAGE_RETENTION_DAYS: ${SKILLGO_STORAGE_RETENTION_DAYS:-15} SKILLGO_STORAGE_CLEANUP_INTERVAL_SECONDS: ${SKILLGO_STORAGE_CLEANUP_INTERVAL_SECONDS:-3600} SKILLGO_STORAGE_ORPHAN_GRACE_HOURS: ${SKILLGO_STORAGE_ORPHAN_GRACE_HOURS:-24} SKILLGO_CONTEXT_MAX_MESSAGES: ${SKILLGO_CONTEXT_MAX_MESSAGES:-20} SKILLGO_CONTEXT_MAX_CHARS: ${SKILLGO_CONTEXT_MAX_CHARS:-30000} SKILLGO_CONVERSATION_LOCK_SECONDS: ${SKILLGO_CONVERSATION_LOCK_SECONDS:-180} SKILLGO_WORKSPACE_MAX_FILE_BYTES: ${SKILLGO_WORKSPACE_MAX_FILE_BYTES:-10485760} SKILLGO_WORKSPACE_MAX_FILES: ${SKILLGO_WORKSPACE_MAX_FILES:-30} SKILLGO_WORKSPACE_EXTRACT_MAX_CHARS: ${SKILLGO_WORKSPACE_EXTRACT_MAX_CHARS:-200000} SKILLGO_WORKSPACE_CONTEXT_MAX_CHARS: ${SKILLGO_WORKSPACE_CONTEXT_MAX_CHARS:-40000} SKILLGO_CORS_ORIGINS: ${SKILLGO_CORS_ORIGINS:-http://localhost:8080} # Skill package upload validation runs in the API. SKILLGO_MAX_UPLOAD_BYTES: ${SKILLGO_MAX_UPLOAD_BYTES:-52428800} SKILLGO_MAX_UNCOMPRESSED_BYTES: ${SKILLGO_MAX_UNCOMPRESSED_BYTES:-262144000} SKILLGO_MAX_ARCHIVE_FILES: ${SKILLGO_MAX_ARCHIVE_FILES:-500} volumes: - skillgo-storage:/var/lib/skillgo/storage networks: - backend - model-egress depends_on: db: condition: service_healthy cap_drop: ["ALL"] security_opt: ["no-new-privileges:true"] read_only: true tmpfs: # Starlette spools uploaded files >1 MiB to /tmp before the endpoint # reads them: one maxed request is 5 x 200 MiB = ~1 GiB, so 2g gives # 2x headroom (RAM-backed, vanishes on restart). - /tmp:rw,nosuid,nodev,noexec,size=2g pids_limit: 256 # Spool (tmpfs counts here) + in-memory file bytes + transients + baseline # for one maxed 5x200 MiB upload request: ~2.5g. 4g keeps headroom. mem_limit: 4g cpus: 1.0 healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=2)"] interval: 5s timeout: 3s retries: 20 restart: unless-stopped worker: build: context: ./backend command: ["python", "-m", "app.sandbox_worker"] user: "10001:10001" group_add: - "${SKILLGO_DOCKER_GID:-999}" environment: SKILLGO_ENVIRONMENT: production SKILLGO_ENVIRONMENT_PREPARATION_ENABLED: ${SKILLGO_ENVIRONMENT_PREPARATION_ENABLED:-false} SKILLGO_ENVIRONMENT_BASE_IMAGE: ${SKILLGO_ENVIRONMENT_BASE_IMAGE:-} SKILLGO_ENVIRONMENT_QUEUE_WAIT_SECONDS: ${SKILLGO_ENVIRONMENT_QUEUE_WAIT_SECONDS:-3600} SKILLGO_DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-skillgo}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-skillgo} SKILLGO_STORAGE_ROOT: /var/lib/skillgo/storage SKILLGO_DURABLE_CHECKPOINTS_ENABLED: ${SKILLGO_DURABLE_CHECKPOINTS_ENABLED:-false} SKILLGO_JWT_SECRET: ${SKILLGO_JWT_SECRET:?set SKILLGO_JWT_SECRET} SKILLGO_MODEL_BASE_URL: ${SKILLGO_MODEL_BASE_URL:-} SKILLGO_MODEL_API_KEY: ${SKILLGO_MODEL_API_KEY:-} SKILLGO_MODEL_NAME: ${SKILLGO_MODEL_NAME:-} SKILLGO_MODEL_TIMEOUT_SECONDS: ${SKILLGO_MODEL_TIMEOUT_SECONDS:-0} SKILLGO_MODEL_CONNECT_TIMEOUT_SECONDS: ${SKILLGO_MODEL_CONNECT_TIMEOUT_SECONDS:-15} SKILLGO_MODEL_FIRST_CHUNK_TIMEOUT_SECONDS: ${SKILLGO_MODEL_FIRST_CHUNK_TIMEOUT_SECONDS:-600} SKILLGO_MODEL_STREAM_STALL_TIMEOUT_SECONDS: ${SKILLGO_MODEL_STREAM_STALL_TIMEOUT_SECONDS:-600} SKILLGO_MODEL_TEMPERATURE: ${SKILLGO_MODEL_TEMPERATURE:-0.2} SKILLGO_MODEL_JSON_MODE: ${SKILLGO_MODEL_JSON_MODE:-true} SKILLGO_MODEL_NATIVE_TOOLS: ${SKILLGO_MODEL_NATIVE_TOOLS:-true} SKILLGO_MODEL_TLS_VERIFY: ${SKILLGO_MODEL_TLS_VERIFY:-true} SKILLGO_SANDBOX_WORKER_ENABLED: ${SKILLGO_SANDBOX_WORKER_ENABLED:-false} SKILLGO_SANDBOX_RUNTIME: ${SKILLGO_SANDBOX_RUNTIME:-runsc} SKILLGO_ARTIFACT_DELIVERY_ONLY: ${SKILLGO_ARTIFACT_DELIVERY_ONLY:-true} SKILLGO_CONTEXT_OBSERVABILITY: ${SKILLGO_CONTEXT_OBSERVABILITY:-false} SKILLGO_WORKFLOW_AUTO_DELIVERY_VERSION_IDS: ${SKILLGO_WORKFLOW_AUTO_DELIVERY_VERSION_IDS:-} SKILLGO_DELIVERY_ONLY_SKILL_IDS: ${SKILLGO_DELIVERY_ONLY_SKILL_IDS:-} SKILLGO_SANDBOX_IMAGE: ${SKILLGO_SANDBOX_IMAGE:-skillgo/sandbox-runtime:local} SKILLGO_SANDBOX_POLL_SECONDS: ${SKILLGO_SANDBOX_POLL_SECONDS:-1} SKILLGO_SANDBOX_WORKER_LEASE_SECONDS: ${SKILLGO_SANDBOX_WORKER_LEASE_SECONDS:-90} SKILLGO_SANDBOX_WORKER_HEARTBEAT_SECONDS: ${SKILLGO_SANDBOX_WORKER_HEARTBEAT_SECONDS:-15} SKILLGO_SANDBOX_WORKER_MAX_ATTEMPTS: ${SKILLGO_SANDBOX_WORKER_MAX_ATTEMPTS:-3} SKILLGO_SANDBOX_JOB_TIMEOUT_SECONDS: ${SKILLGO_SANDBOX_JOB_TIMEOUT_SECONDS:-0} SKILLGO_SANDBOX_COMMAND_TIMEOUT_SECONDS: ${SKILLGO_SANDBOX_COMMAND_TIMEOUT_SECONDS:-120} SKILLGO_SANDBOX_MAX_AGENT_TURNS: ${SKILLGO_SANDBOX_MAX_AGENT_TURNS:-300} SKILLGO_SANDBOX_MAX_AGENT_TOOL_CALLS: ${SKILLGO_SANDBOX_MAX_AGENT_TOOL_CALLS:-480} SKILLGO_SANDBOX_MEMORY: ${SKILLGO_SANDBOX_MEMORY:-2g} SKILLGO_SANDBOX_NANO_CPUS: ${SKILLGO_SANDBOX_NANO_CPUS:-1000000000} SKILLGO_SANDBOX_PIDS_LIMIT: ${SKILLGO_SANDBOX_PIDS_LIMIT:-128} SKILLGO_SANDBOX_MAX_ARTIFACT_BYTES: ${SKILLGO_SANDBOX_MAX_ARTIFACT_BYTES:-52428800} volumes: - skillgo-storage:/var/lib/skillgo/storage - /var/run/docker.sock:/var/run/docker.sock networks: - backend - model-egress depends_on: db: condition: service_healthy cap_drop: ["ALL"] security_opt: ["no-new-privileges:true"] read_only: true tmpfs: - /tmp:rw,nosuid,nodev,noexec,size=64m pids_limit: 128 # Workers stream sandbox exec I/O and build durable checkpoint archives; # 512m OOM-killed long-lived workers mid-tool (PPT/image jobs, RSS growth). mem_limit: 2g cpus: 0.5 restart: unless-stopped deploy: replicas: 12 profiles: ["sandbox"] environment-worker: build: context: ./backend command: ["python", "-m", "app.environment_worker"] user: "10001:10001" group_add: ["${SKILLGO_DOCKER_GID:-999}"] environment: SKILLGO_ENVIRONMENT: production SKILLGO_DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-skillgo}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-skillgo} SKILLGO_ENVIRONMENT_PREPARATION_ENABLED: ${SKILLGO_ENVIRONMENT_PREPARATION_ENABLED:-false} SKILLGO_ENVIRONMENT_BASE_IMAGE: ${SKILLGO_ENVIRONMENT_BASE_IMAGE:-} SKILLGO_ENVIRONMENT_BUILD_SECONDS: ${SKILLGO_ENVIRONMENT_BUILD_SECONDS:-900} SKILLGO_SANDBOX_RUNTIME: ${SKILLGO_SANDBOX_RUNTIME:-runsc} volumes: - /var/run/docker.sock:/var/run/docker.sock networks: [backend] depends_on: db: condition: service_healthy cap_drop: ["ALL"] security_opt: ["no-new-privileges:true"] read_only: true tmpfs: ["/tmp:rw,nosuid,nodev,noexec,size=64m"] # Wheel resolution/build spiked above 512m when preparing environments. mem_limit: 1g cpus: 0.5 pids_limit: 128 restart: unless-stopped profiles: ["environments"] sandbox-runtime: image: ${SKILLGO_SANDBOX_IMAGE:-skillgo/sandbox-runtime:local} build: context: ./sandbox-runtime profiles: ["build-only"] web: build: context: ./frontend ports: - "${SKILLGO_WEB_BIND:-127.0.0.1}:${SKILLGO_WEB_PORT:-8080}:8080" networks: - public - backend depends_on: api: condition: service_healthy cap_drop: ["ALL"] security_opt: ["no-new-privileges:true"] read_only: true tmpfs: # nginx scratch space (read_only rootfs). API bodies are streamed through # (proxy_request_buffering/proxy_buffering off in nginx.conf), so this no # longer needs to hold spooled upload bodies. - /var/cache/nginx:rw,nosuid,nodev,noexec,size=96m,uid=101,gid=101,mode=0750 - /var/run:rw,nosuid,nodev,noexec,size=4m,uid=101,gid=101,mode=0750 pids_limit: 128 mem_limit: 256m cpus: 0.5 restart: unless-stopped networks: public: backend: internal: true model-egress: # Control-plane egress for the configured private model. Skill runtime # sandboxes must not inherit this network. volumes: skillgo-postgres: skillgo-storage: