# Chrysopedia — Docker Compose # XPLTD convention: xpltd_chrysopedia project, bind mounts, dedicated bridge # Deployed to: /vmPool/r/compose/xpltd_chrysopedia/ (symlinked) name: xpltd_chrysopedia services: # ── PostgreSQL 16 ── chrysopedia-db: image: postgres:16-alpine container_name: chrysopedia-db restart: unless-stopped environment: POSTGRES_USER: ${POSTGRES_USER:-chrysopedia} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme} POSTGRES_DB: ${POSTGRES_DB:-chrysopedia} volumes: - /vmPool/r/services/chrysopedia_db:/var/lib/postgresql/data ports: - "127.0.0.1:5433:5432" networks: - chrysopedia healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-chrysopedia}"] interval: 10s timeout: 5s retries: 5 stop_grace_period: 30s # ── Redis (Celery broker + runtime config) ── chrysopedia-redis: image: redis:7-alpine container_name: chrysopedia-redis restart: unless-stopped command: redis-server --save 60 1 --loglevel warning volumes: - /vmPool/r/services/chrysopedia_redis:/data networks: - chrysopedia healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 stop_grace_period: 15s # ── Qdrant vector database ── chrysopedia-qdrant: image: qdrant/qdrant:v1.13.2 container_name: chrysopedia-qdrant restart: unless-stopped volumes: - /vmPool/r/services/chrysopedia_qdrant:/qdrant/storage networks: - chrysopedia healthcheck: test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/localhost/6333'"] interval: 15s timeout: 5s retries: 5 start_period: 10s stop_grace_period: 30s # ── Ollama (embedding model server) ── chrysopedia-ollama: image: ollama/ollama:latest container_name: chrysopedia-ollama restart: unless-stopped volumes: - /vmPool/r/services/chrysopedia_ollama:/root/.ollama networks: - chrysopedia healthcheck: test: ["CMD", "ollama", "list"] interval: 15s timeout: 5s retries: 5 start_period: 30s stop_grace_period: 15s # ── LightRAG (graph-based RAG knowledge base) ── chrysopedia-lightrag: image: ghcr.io/hkuds/lightrag:latest container_name: chrysopedia-lightrag restart: unless-stopped env_file: - path: .env.lightrag required: true environment: TIKTOKEN_CACHE_DIR: /app/data/tiktoken ports: - "127.0.0.1:9621:9621" volumes: - /vmPool/r/services/chrysopedia_lightrag:/app/data depends_on: chrysopedia-qdrant: condition: service_healthy chrysopedia-ollama: condition: service_healthy networks: - chrysopedia healthcheck: test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:9621/health')\" || exit 1"] interval: 15s timeout: 5s retries: 5 start_period: 30s stop_grace_period: 15s # ── FastAPI application ── chrysopedia-api: build: context: . dockerfile: docker/Dockerfile.api container_name: chrysopedia-api restart: unless-stopped env_file: - path: .env required: false environment: DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-chrysopedia}:${POSTGRES_PASSWORD:-changeme}@chrysopedia-db:5432/${POSTGRES_DB:-chrysopedia} REDIS_URL: redis://chrysopedia-redis:6379/0 QDRANT_URL: http://chrysopedia-qdrant:6333 EMBEDDING_API_URL: http://chrysopedia-ollama:11434/v1 LLM_FALLBACK_URL: http://chrysopedia-ollama:11434/v1 LLM_FALLBACK_MODEL: ${LLM_FALLBACK_MODEL:-qwen2.5:7b} BASE_URL: ${BASE_URL:-http://ub01:8096} PROMPTS_PATH: /prompts volumes: - /vmPool/r/services/chrysopedia_data:/data - ./config:/config:ro - /vmPool/r/services/chrysopedia_videos:/videos:ro depends_on: chrysopedia-db: condition: service_healthy chrysopedia-redis: condition: service_healthy chrysopedia-qdrant: condition: service_healthy chrysopedia-ollama: condition: service_healthy networks: - chrysopedia stop_grace_period: 15s # ── Celery worker (pipeline stages 2-6) ── chrysopedia-worker: build: context: . dockerfile: docker/Dockerfile.api container_name: chrysopedia-worker restart: unless-stopped env_file: - path: .env required: false environment: DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-chrysopedia}:${POSTGRES_PASSWORD:-changeme}@chrysopedia-db:5432/${POSTGRES_DB:-chrysopedia} REDIS_URL: redis://chrysopedia-redis:6379/0 QDRANT_URL: http://chrysopedia-qdrant:6333 EMBEDDING_API_URL: http://chrysopedia-ollama:11434/v1 LLM_FALLBACK_URL: http://chrysopedia-ollama:11434/v1 LLM_FALLBACK_MODEL: ${LLM_FALLBACK_MODEL:-qwen2.5:7b} BASE_URL: ${BASE_URL:-http://ub01:8096} PROMPTS_PATH: /prompts command: ["celery", "-A", "worker", "worker", "--beat", "--loglevel=info", "--concurrency=1"] healthcheck: test: ["CMD-SHELL", "celery -A worker inspect ping --timeout=5 2>/dev/null | grep -q pong || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 30s volumes: - /vmPool/r/services/chrysopedia_data:/data - ./prompts:/prompts:ro - ./config:/config:ro - /vmPool/r/services/chrysopedia_videos:/videos:ro depends_on: chrysopedia-db: condition: service_healthy chrysopedia-redis: condition: service_healthy chrysopedia-qdrant: condition: service_healthy chrysopedia-ollama: condition: service_healthy networks: - chrysopedia stop_grace_period: 30s # ── Transcript folder watcher ── chrysopedia-watcher: build: context: . dockerfile: docker/Dockerfile.api container_name: chrysopedia-watcher restart: unless-stopped command: ["python", "watcher.py"] environment: WATCHER_API_URL: http://chrysopedia-api:8000/api/v1/ingest WATCH_FOLDER: /watch volumes: - /vmPool/r/services/chrysopedia_watch:/watch depends_on: chrysopedia-api: condition: service_healthy networks: - chrysopedia healthcheck: test: ["CMD-SHELL", "python -c \"import os; os.kill(1, 0)\" || exit 1"] interval: 30s timeout: 5s retries: 3 start_period: 15s stop_grace_period: 15s # ── MinIO (file storage for post attachments) ── chrysopedia-minio: image: minio/minio container_name: chrysopedia-minio restart: unless-stopped command: server /data --console-address ":9001" environment: MINIO_ROOT_USER: ${MINIO_ROOT_USER:-chrysopedia} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-changeme-minio} volumes: - /vmPool/r/services/chrysopedia_minio:/data networks: - chrysopedia healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:9000/minio/health/live || exit 1"] interval: 15s timeout: 5s retries: 5 start_period: 10s stop_grace_period: 15s # ── React web UI (nginx) ── chrysopedia-web: build: context: . dockerfile: docker/Dockerfile.web args: VITE_GIT_COMMIT: ${GIT_COMMIT_SHA:-dev} container_name: chrysopedia-web-8096 restart: unless-stopped ports: - "0.0.0.0:8096:80" depends_on: - chrysopedia-api networks: - chrysopedia healthcheck: test: ["CMD-SHELL", "curl -sf http://127.0.0.1:80/ || exit 1"] interval: 30s timeout: 5s retries: 3 start_period: 10s stop_grace_period: 15s # ── MCP Server (management tools for AI agents) ── chrysopedia-mcp: build: context: . dockerfile: docker/Dockerfile.mcp container_name: chrysopedia-mcp restart: unless-stopped environment: DATABASE_URL: postgresql://${POSTGRES_USER:-chrysopedia}:${POSTGRES_PASSWORD:-changeme}@chrysopedia-db:5432/${POSTGRES_DB:-chrysopedia} REDIS_URL: redis://chrysopedia-redis:6379/0 QDRANT_URL: http://chrysopedia-qdrant:6333 API_URL: http://chrysopedia-api:8000 PROMPTS_PATH: /prompts ports: - "0.0.0.0:8101:8101" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./prompts:/prompts:ro depends_on: chrysopedia-db: condition: service_healthy chrysopedia-redis: condition: service_healthy chrysopedia-api: condition: service_healthy networks: - chrysopedia healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:8101/mcp -o /dev/null || exit 1"] interval: 30s timeout: 5s retries: 3 start_period: 15s stop_grace_period: 15s networks: chrysopedia: driver: bridge ipam: config: - subnet: "172.32.0.0/24"