Ctrl K
ComunidadGuías…

docker-compose.yml

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

Archivo original ↗Preguntar a KiBot
# ============================================
# DeepTutor Docker Compose Configuration
# ============================================
# This file provides orchestration for DeepTutor services
#
# Usage:
#   Production:  python scripts/docker_compose.py up -d
#   Development: python scripts/docker_compose.py -f docker-compose.yml -f docker-compose.dev.yml up
#   Build only:  python scripts/docker_compose.py build
#
# Prerequisites:
#   1. Runtime settings are stored in ./data/user/settings (created automatically)
#   2. Configure model providers from the web Settings page or model_catalog.json
#   3. Use scripts/docker_compose.py so port mappings are rendered from system.json
#
# Local LLM (LM Studio / Ollama / vLLM):
#   Use "host.docker.internal" instead of "localhost" in provider base_url fields.
#   Configure provider endpoints in data/user/settings/model_catalog.json or the UI.
#
# Content workspace:
#   DEEPTUTOR_WORKSPACE_HOST=/absolute/host/folder python scripts/docker_compose.py up -d
#   maps that folder to the stable in-container path /workspace. If omitted,
#   ./data/user/workspace remains the default.
# ============================================

services:
  # Redis is internal-only runtime coordination state. AOF keeps unflushed
  # event/command streams available across a sidecar restart; business data
  # remains in SQLite/PocketBase.
  redis:
    image: redis:7.4-alpine
    container_name: deeptutor-redis
    restart: unless-stopped
    command: ["redis-server", "--appendonly", "yes", "--appendfsync", "everysec"]
    volumes:
      - ./data/redis:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 10
    networks:
      - deeptutor-network

  # ============================================
  # PocketBase — optional auth + storage sidecar
  # Set integrations.pocketbase_url=http://pocketbase:8090 to activate.
  # Leave integrations.pocketbase_url blank to run without PocketBase (SQLite fallback).
  # ============================================
  pocketbase:
    image: ghcr.io/muchobien/pocketbase:latest
    container_name: pocketbase
    restart: unless-stopped
    ports:
      - "${DEEPTUTOR_DOCKER_POCKETBASE_PORT:-8090}:8090"
    volumes:
      # The upstream pocketbase image's entrypoint uses absolute paths
      # (no /pb/ prefix): --dir=/pb_data --publicDir=/pb_public
      # --hooksDir=/pb_hooks. The previous example mounted at /pb/pb_data
      # and crashed on first start with "mkdir /pb_data: read-only file
      # system".
      - ./data/pocketbase:/pb_data
      - ./data/pocketbase/public:/pb_public
      - ./data/pocketbase/hooks:/pb_hooks
    networks:
      - deeptutor-network
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8090/api/health"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 10s

  # ============================================
  # All-in-One DeepTutor Service
  # ============================================
  deeptutor:
    build:
      context: .
      dockerfile: Dockerfile
      target: production
    container_name: deeptutor
    restart: unless-stopped
    ports:
      - "${DEEPTUTOR_DOCKER_BACKEND_PORT:-8001}:${DEEPTUTOR_DOCKER_BACKEND_PORT:-8001}"
      - "${DEEPTUTOR_DOCKER_FRONTEND_PORT:-3782}:${DEEPTUTOR_DOCKER_FRONTEND_PORT:-3782}"

    volumes:
      # Everything DeepTutor persists lives under ./data — one tree to mount
      # and back up: admin workspace + runtime settings (data/user), per-user
      # workspaces (data/users), partners (data/partners), accounts/grants/
      # audit (data/system), knowledge bases, memory.
      # The sandbox-runner gets a narrower slice of this volume: never
      # data/system, which holds auth state, provider API keys, and per-owner
      # secrets. What it *does* carry for non-admin accounts is wider than
      # workspace subtrees — see the scope note on that service's own volumes.
      - ./data:/app/data
      - "${DEEPTUTOR_WORKSPACE_HOST:-./data/user/workspace}:/workspace"

    environment:
      # Route untrusted shell exec to the runner sidecar (SYSTEM isolation).
      # When this is set, the main app NEVER runs untrusted code in its own
      # process — see deeptutor/services/sandbox/config.py:build_backend().
      # On bare-metal / macOS deployments where the runner is not started, leave
      # this unset: the app degrades to bwrap (Linux, if installed) or, only when
      # DEEPTUTOR_SANDBOX_ALLOW_SUBPROCESS=1, a restricted subprocess.
      - DEEPTUTOR_SANDBOX_RUNNER_URL=http://sandbox-runner:8900
      - DEEPTUTOR_WORKSPACE_ROOT=/workspace
      - DEEPTUTOR_WORKSPACE_ALLOWED_ROOTS=/workspace

      # Optional dependencies, re-applied on every start (#762). Installing
      # these with `docker exec` does not survive `compose down`, so declare
      # them here instead. Both are idempotent and never fatal; the pip cache
      # lives on the data volume, so a rebuild reuses what it already fetched.
      # Extras come from pyproject.toml — preview one with
      # `python scripts/install_extras.py --dry-run "<name>"`.
      # - DEEPTUTOR_EXTRAS=math-animator,partners
      # - DEEPTUTOR_APT_PACKAGES=ffmpeg

      # Unraid / NAS bind mounts: set these to the host owner of ./data
      # instead of chowning the volume or running the app as root.
      # The entrypoint remaps the unprivileged `deeptutor` user to these ids.
      # - PUID=99
      # - PGID=100

    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:${DEEPTUTOR_DOCKER_BACKEND_PORT:-8001}/health/ready"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

    depends_on:
      redis:
        condition: service_healthy
      pocketbase:
        condition: service_healthy
      sandbox-runner:
        condition: service_healthy

    networks:
      - deeptutor-network

  # ============================================
  # Sandbox Runner Sidecar
  # ============================================
  # Executes untrusted shell commands in an isolated, least-privileged
  # container so the main app never has to. Rationale for the sidecar shape:
  #   * the main `deeptutor` container keeps minimal privileges and does NOT
  #     mount the docker socket or run untrusted code in its own process;
  #   * a sandbox escape here lands in a stripped, unprivileged container with
  #     no app secrets, not in the main app.
  # Not exposed to the host: it is reachable only on the internal
  # deeptutor-network at http://sandbox-runner:8900.
  sandbox-runner:
    build:
      context: .
      dockerfile: Dockerfile.runner
    container_name: deeptutor-sandbox-runner
    restart: unless-stopped

    # Share the content workspace at the SAME stable path as the main app.
    # The whole tree is readable so workspace_list/read and shell tools can use
    # user files. Only outputs/ is writable: a command cannot modify source
    # material, user settings, or any other host path. The runner never sees
    # data/system, data/users, or the rest of the application data tree.
    volumes:
      - "${DEEPTUTOR_WORKSPACE_HOST:-./data/user/workspace}:/workspace:ro"
      - "${DEEPTUTOR_WORKSPACE_HOST:-./data/user/workspace}/outputs:/workspace/outputs"
      # Installed CLI apps, READ-ONLY. This is the one directory the runner
      # executes code from that it did not receive in a request, so it must not
      # be writable here: an escaped command that could overwrite an app's entry
      # point would run as that app in every later turn, for every account.
      # Installing writes it from the main container instead (services/cli_apps).
      - ./data/cli-apps:/app/data/cli-apps:ro

    environment:
      - DEEPTUTOR_RUNNER_ALLOWED_WORKDIRS=/workspace/outputs

    # No `ports:` — intentionally not published to the host.

    # ----- Security hardening -----
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    # Read-only root filesystem: the runner only needs to read its own code and
    # write under the shared volume (and scratch space). Give it tmpfs for the
    # paths shell tooling expects to be writable (/tmp, $HOME). If a workload
    # needs more writable scratch, relax read_only rather than widening tmpfs.
    read_only: true
    tmpfs:
      - /tmp
      - /home/runner
    pids_limit: 256
    mem_limit: 1g
    # cgroup-enforced RSS cap is the authoritative memory backstop; the
    # per-command RLIMIT_AS in server.py is a cheaper secondary guard.

    healthcheck:
      test: ["CMD", "python", "-c",
             "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:8900/health',timeout=3).status==200 else 1)"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 5s

    networks:
      - deeptutor-network

# ============================================
# Networks
# ============================================
networks:
  deeptutor-network:
    driver: bridge