services: frontend: image: wechatopenai/weknora-ui:${WEKNORA_VERSION:-latest} build: context: ./frontend args: - MAX_FILE_SIZE_MB=${MAX_FILE_SIZE_MB:-50} container_name: WeKnora-frontend ports: - "${FRONTEND_PORT:-80}:80" environment: - MAX_FILE_SIZE_MB=${MAX_FILE_SIZE_MB:-50} - APP_HOST=${APP_HOST:-app} # APP_BACKEND_PORT: the port NGINX proxies to (default 8080). # For local deployment this is the App container's listening port, independent of host-mapped APP_PORT. # For remote deployment, set this to the remote App's service port. - APP_PORT=${APP_BACKEND_PORT:-8080} - APP_SCHEME=${APP_SCHEME:-http} # NOTE: If using a remote App backend, comment out or remove the depends_on # block below and set APP_HOST/APP_BACKEND_PORT/APP_SCHEME in your .env file. depends_on: app: condition: service_healthy networks: - WeKnora-network restart: unless-stopped app: image: wechatopenai/weknora-app:${WEKNORA_VERSION:-latest} build: context: . dockerfile: docker/Dockerfile.app args: - APK_MIRROR_ARG=${APK_MIRROR_ARG:-} container_name: WeKnora-app ports: - "${APP_PORT:-8080}:8080" volumes: - data-files:/data/files - docreader-tmp:/tmp/docreader:ro # Mount custom config file (required for IM integration) - ./config/config.yaml:/app/config/config.yaml # Optional: mount custom skills directory (allows adding skills without rebuilding image) - ./skills/preloaded:/app/skills/preloaded healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 30s timeout: 10s retries: 3 start_period: 60s environment: - LOG_LEVEL=${LOG_LEVEL:-} - COS_SECRET_ID=${COS_SECRET_ID:-} - COS_SECRET_KEY=${COS_SECRET_KEY:-} - COS_REGION=${COS_REGION:-} - COS_BUCKET_NAME=${COS_BUCKET_NAME:-} - COS_APP_ID=${COS_APP_ID:-} - COS_PATH_PREFIX=${COS_PATH_PREFIX:-} - COS_ENABLE_OLD_DOMAIN=${COS_ENABLE_OLD_DOMAIN:-} - GIN_MODE=${GIN_MODE:-release} - DISABLE_REGISTRATION=${DISABLE_REGISTRATION:-false} - DB_DRIVER=postgres - DB_HOST=postgres - DB_PORT=5432 - DB_USER=${DB_USER:-} - DB_PASSWORD=${DB_PASSWORD:-} - DB_NAME=${DB_NAME:-} - TZ=Asia/Shanghai - OTEL_EXPORTER_OTLP_ENDPOINT=jaeger:4317 - OTEL_SERVICE_NAME=WeKnora - OTEL_TRACES_EXPORTER=otlp - OTEL_METRICS_EXPORTER=none - OTEL_LOGS_EXPORTER=none - OTEL_PROPAGATORS=tracecontext,baggage - RETRIEVE_DRIVER=${RETRIEVE_DRIVER:-} - ELASTICSEARCH_ADDR=${ELASTICSEARCH_ADDR:-} - ELASTICSEARCH_USERNAME=${ELASTICSEARCH_USERNAME:-} - ELASTICSEARCH_PASSWORD=${ELASTICSEARCH_PASSWORD:-} - ELASTICSEARCH_INDEX=${ELASTICSEARCH_INDEX:-} - QDRANT_HOST=qdrant - QDRANT_PORT=${QDRANT_PORT:-6334} - QDRANT_COLLECTION=${QDRANT_COLLECTION:-weknora_embeddings} - QDRANT_API_KEY=${QDRANT_API_KEY:-} - QDRANT_USE_TLS=${QDRANT_USE_TLS:-false} - MILVUS_ADDRESS=milvus:19530 - MILVUS_COLLECTION=${MILVUS_COLLECTION:-weknora_embeddings} - DOCREADER_ADDR=${DOCREADER_ADDR:-docreader:50051} - DOCREADER_TRANSPORT=${DOCREADER_TRANSPORT:-grpc} - WEAVIATE_HOST=${WEAVIATE_HOST:-weaviate:8080} - WEAVIATE_GRPC_ADDRESS=${WEAVIATE_GRPC_ADDRESS:-weaviate:50051} - WEAVIATE_SCHEME=${WEAVIATE_SCHEME:-http} - WEAVIATE_AUTH_ENABLED=${WEAVIATE_AUTH_ENABLED:-false} - WEAVIATE_API_KEY=${WEAVIATE_API_KEY:-} - STORAGE_TYPE=${STORAGE_TYPE:-} - LOCAL_STORAGE_BASE_DIR=${LOCAL_STORAGE_BASE_DIR:-} - AUTO_RECOVER_DIRTY=${AUTO_RECOVER_DIRTY:-true} - MINIO_ENDPOINT=minio:9000 - MINIO_ACCESS_KEY_ID=${MINIO_ACCESS_KEY_ID:-minioadmin} - MINIO_SECRET_ACCESS_KEY=${MINIO_SECRET_ACCESS_KEY:-minioadmin} - MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME:-} - OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://host.docker.internal:11434} - STREAM_MANAGER_TYPE=${STREAM_MANAGER_TYPE:-} - REDIS_ADDR=redis:6379 - REDIS_USERNAME=${REDIS_USERNAME:-} - REDIS_PASSWORD=${REDIS_PASSWORD:-} - REDIS_DB=${REDIS_DB:-} - REDIS_PREFIX=${REDIS_PREFIX:-} - ENABLE_GRAPH_RAG=${ENABLE_GRAPH_RAG:-} - NEO4J_ENABLE=${NEO4J_ENABLE:-} - NEO4J_URI=bolt://neo4j:7687 - NEO4J_USERNAME=${NEO4J_USERNAME:-neo4j} - NEO4J_PASSWORD=${NEO4J_PASSWORD:-password} - TENANT_AES_KEY=${TENANT_AES_KEY:-} - SYSTEM_AES_KEY=${SYSTEM_AES_KEY:-} - CONCURRENCY_POOL_SIZE=${CONCURRENCY_POOL_SIZE:-5} - JWT_SECRET=${JWT_SECRET:-} # IM integration - FEISHU_APP_ID=${FEISHU_APP_ID:-} - FEISHU_APP_SECRET=${FEISHU_APP_SECRET:-} - WECOM_BOT_ID=${WECOM_BOT_ID:-} - WECOM_BOT_SECRET=${WECOM_BOT_SECRET:-} # File size limit (in MB) - MAX_FILE_SIZE_MB=${MAX_FILE_SIZE_MB:-50} # Agent Skills Sandbox - WEKNORA_SANDBOX_MODE=${WEKNORA_SANDBOX_MODE:-docker} - WEKNORA_SANDBOX_TIMEOUT=${WEKNORA_SANDBOX_TIMEOUT:-60} - WEKNORA_SANDBOX_DOCKER_IMAGE=${WEKNORA_SANDBOX_DOCKER_IMAGE:-wechatopenai/weknora-sandbox:${WEKNORA_VERSION:-latest}} - APK_MIRROR_ARG=${APK_MIRROR_ARG:-} depends_on: redis: condition: service_started postgres: condition: service_healthy docreader: condition: service_healthy networks: - WeKnora-network restart: unless-stopped extra_hosts: - "host.docker.internal:host-gateway" # Sandbox 镜像:仅用于 build/pull,非常驻服务;app 执行 Skills 时按需 docker run 该镜像,用毕即释 sandbox: image: wechatopenai/weknora-sandbox:${WEKNORA_VERSION:-latest} container_name: WeKnora-sandbox build: context: . dockerfile: docker/Dockerfile.sandbox profiles: - full command: ["true"] restart: "no" docreader: image: wechatopenai/weknora-docreader:${WEKNORA_VERSION:-latest} build: context: . dockerfile: docker/Dockerfile.docreader args: - APT_MIRROR=${APT_MIRROR:-} container_name: WeKnora-docreader ports: - "${DOCREADER_PORT:-50051}:50051" volumes: - docreader-tmp:/tmp/docreader environment: - DOCREADER_IMAGE_OUTPUT_DIR=/tmp/docreader - MAX_FILE_SIZE_MB=${MAX_FILE_SIZE_MB:-} healthcheck: test: ["CMD", "grpc_health_probe", "-addr=:50051"] interval: 30s timeout: 10s retries: 3 start_period: 60s networks: - WeKnora-network restart: unless-stopped extra_hosts: - "host.docker.internal:host-gateway" # 修改的PostgreSQL配置 postgres: image: paradedb/paradedb:v0.21.4-pg17 container_name: WeKnora-postgres environment: - POSTGRES_USER=${DB_USER} - POSTGRES_PASSWORD=${DB_PASSWORD} - POSTGRES_DB=${DB_NAME} volumes: - postgres-data:/var/lib/postgresql/data networks: - WeKnora-network healthcheck: test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"] interval: 10s # 增加时间间隔 timeout: 10s # 增加超时时间 retries: 3 # 减少重试次数,让失败更快反馈 start_period: 30s # 给予初始启动更多时间 restart: unless-stopped # 添加停机时的优雅退出时间 stop_grace_period: 1m redis: image: redis:7.0-alpine container_name: WeKnora-redis command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD} restart: always networks: - WeKnora-network minio: image: minio/minio:RELEASE.2025-09-07T16-13-09Z container_name: WeKnora-minio ports: - "${MINIO_PORT:-9000}:9000" - "${MINIO_CONSOLE_PORT:-9001}:9001" environment: - MINIO_ROOT_USER=${MINIO_ACCESS_KEY_ID:-minioadmin} - MINIO_ROOT_PASSWORD=${MINIO_SECRET_ACCESS_KEY:-minioadmin} command: server --console-address ":9001" /data volumes: - minio_data:/data healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 30s timeout: 20s retries: 3 networks: - WeKnora-network profiles: - minio - full jaeger: image: jaegertracing/all-in-one:1.76.0 container_name: WeKnora-jaeger ports: - "6831:6831/udp" # Jaeger Thrift接收器 - "6832:6832/udp" # Jaeger Thrift接收器(Compact) - "5778:5778" # 配置端口 - "16686:16686" # Web UI - "4317:4317" # OTLP gRPC接收器 - "4318:4318" # OTLP HTTP接收器 - "14250:14250" # 接收模型端口 - "14268:14268" # Jaeger HTTP接收器 - "9411:9411" # Zipkin兼容性端口 environment: - COLLECTOR_OTLP_ENABLED=true - COLLECTOR_ZIPKIN_HOST_PORT=:9411 volumes: - jaeger_data:/var/lib/jaeger # 持久化 Jaeger 数据 networks: - WeKnora-network restart: unless-stopped profiles: - jaeger - full neo4j: image: neo4j:2025.10.1 container_name: WeKnora-neo4j volumes: - neo4j-data:/data environment: - NEO4J_AUTH=${NEO4J_USERNAME:-neo4j}/${NEO4J_PASSWORD:-password} - NEO4J_apoc_export_file_enabled=true - NEO4J_apoc_import_file_enabled=true - NEO4J_apoc_import_file_use__neo4j__config=true - NEO4JLABS_PLUGINS=["apoc"] ports: - "7474:7474" - "7687:7687" restart: always networks: - WeKnora-network profiles: - neo4j - full qdrant: image: qdrant/qdrant:v1.16.2 container_name: WeKnora-qdrant ports: - "${QDRANT_REST_PORT:-6333}:6333" - "${QDRANT_PORT:-6334}:6334" volumes: - qdrant_data:/qdrant/storage networks: - WeKnora-network restart: unless-stopped profiles: - qdrant - full milvus: image: milvusdb/milvus:v2.6.11 container_name: WeKnora-milvus security_opt: - seccomp:unconfined command: ["milvus", "run", "standalone"] environment: - ETCD_USE_EMBED=true - ETCD_DATA_DIR=/var/lib/milvus/etcd - COMMON_STORAGETYPE=local - DEPLOY_MODE=STANDALONE healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"] interval: 30s start_period: 90s timeout: 20s retries: 3 ports: - "19530:19530" - "9091:9091" volumes: - milvus_data:/var/lib/milvus networks: - WeKnora-network restart: unless-stopped profiles: - milvus weaviate: image: semitechnologies/weaviate:1.28.4 container_name: WeKnora-weaviate environment: - PERSISTENCE_DATA_PATH=/var/lib/weaviate - CLUSTER_HOSTNAME=node1 - DEFAULT_VECTORIZER_MODULE=none - ENABLE_MODULES=none - AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=true - CLUSTER_GOSSIP_BIND_PORT=7000 - CLUSTER_DATA_BIND_PORT=7001 - RAFT_BOOTSTRAP_EXPECT=1 ports: - "9035:8080" - "50052:50051" volumes: - weaviate_data:/var/lib/weaviate networks: - WeKnora-network restart: unless-stopped profiles: - weaviate networks: WeKnora-network: driver: bridge volumes: postgres-data: data-files: docreader-tmp: jaeger_data: minio_data: neo4j-data: qdrant_data: milvus_data: weaviate_data: