Files
enkly-ai/docker-compose.yml
enkly 79ce8dc4c0
Some checks failed
Go CI Pipeline / build_and_test (push) Failing after 22m0s
Init Project
2026-10-30 18:06:55 +01:00

257 lines
10 KiB
YAML

services:
# ---------------------------------------------------------------------
# Infrastructure
# ---------------------------------------------------------------------
rabbitmq:
image: rabbitmq:3.13-management-alpine
ports: ["5672:5672", "15672:15672"]
environment:
RABBITMQ_DEFAULT_USER: enkly
RABBITMQ_DEFAULT_PASS: enkly
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
interval: 10s
timeout: 5s
retries: 10
# vLLM OpenAI-compatible inference servers. One container per model —
# a vLLM engine process serves exactly one model, so chat (Qwen,
# tool-calling + reasoning) and embeddings (Nomic) are two separate
# services with independent lifecycles, rather than the two "tags"
# one shared Ollama daemon used to serve. Both need an NVIDIA GPU and
# the nvidia container runtime (`docker info | grep nvidia` to check)
# — on a CPU-only dev box, run `docker compose --profile cpu up` isn't
# provided here because vLLM's CPU backend is a materially different
# build/image; see https://docs.vllm.ai for that path if you need it.
#
# Pin the image tag to a version you've actually tested — "latest" on
# a GPU inference server is how a routine `docker compose pull`
# becomes an unplanned model-serving outage. v0.31.0 below is this
# deployment's current pin (matches k8s/11-vllm.yaml); bump
# deliberately. Must stay >= the first tag with Qwen3.5 model
# support or the server fails to load with an unrecognized
# architecture error.
vllm-chat:
image: vllm/vllm-openai:v0.31.0
command:
- --model=Qwen/Qwen3.5-35B-A3B-GPTQ-Int4
- --served-model-name=${ENKLY_CHAT_MODEL:-qwen3.5}
- --quantization=gptq_marlin
- --tool-call-parser=qwen3_coder
- --enable-auto-tool-choice
- --reasoning-parser=qwen3
- --max-model-len=32768
- --gpu-memory-utilization=0.90
- --tensor-parallel-size=${ENKLY_CHAT_TP_SIZE:-1}
- --api-key=${ENKLY_VLLM_API_KEY:-}
ports: ["8000:8000"]
volumes: ["hf-cache:/root/.cache/huggingface"]
environment:
HUGGING_FACE_HUB_TOKEN: "${HUGGING_FACE_HUB_TOKEN:-}"
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: ${ENKLY_CHAT_TP_SIZE:-1}
capabilities: [gpu]
# vLLM's own /dev/shm requirement for NCCL/CUDA-graph scratch space
# — without this, multi-GPU (tensor-parallel) startup or even
# single-GPU CUDA-graph capture can fail or silently fall back to a
# slower path. 2gb is generous for a single-GPU chat replica; raise
# it if ENKLY_CHAT_TP_SIZE > 1.
shm_size: "2gb"
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:8000/health').status==200 else 1)"]
# Model load (Qwen3.5-35B-A3B-GPTQ-Int4, ~20GB on disk) can still
# take a few minutes from a cold Hugging Face cache — a short
# interval just means more log noise while it warms up, not a
# faster start.
interval: 15s
timeout: 5s
retries: 40
start_period: 10m
vllm-embed:
image: vllm/vllm-openai:v0.6.3
command:
- --model=nomic-ai/nomic-embed-text-v1.5
- --served-model-name=${ENKLY_EMBED_MODEL:-nomic-embed-text}
- --task=embed
- --trust-remote-code
- --gpu-memory-utilization=0.30
ports: ["8001:8000"]
volumes: ["hf-cache:/root/.cache/huggingface"]
environment:
HUGGING_FACE_HUB_TOKEN: "${HUGGING_FACE_HUB_TOKEN:-}"
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
shm_size: "1gb"
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:8000/health').status==200 else 1)"]
interval: 10s
timeout: 5s
retries: 30
start_period: 3m
# Database-per-service: four separate Postgres databases, never one
# shared instance a service could accidentally cross-query. knowledge
# uses the pgvector image; the other three a bare postgres image.
iam-db:
image: postgres:16-alpine
environment: {POSTGRES_USER: enkly, POSTGRES_PASSWORD: enkly, POSTGRES_DB: enkly_iam}
volumes: ["iam-db-data:/var/lib/postgresql/data"]
healthcheck: {test: ["CMD-SHELL", "pg_isready -U enkly"], interval: 5s, timeout: 5s, retries: 10}
billing-db:
image: postgres:16-alpine
environment: {POSTGRES_USER: enkly, POSTGRES_PASSWORD: enkly, POSTGRES_DB: enkly_billing}
volumes: ["billing-db-data:/var/lib/postgresql/data"]
healthcheck: {test: ["CMD-SHELL", "pg_isready -U enkly"], interval: 5s, timeout: 5s, retries: 10}
conversation-db:
image: postgres:16-alpine
environment: {POSTGRES_USER: enkly, POSTGRES_PASSWORD: enkly, POSTGRES_DB: enkly_conversation}
volumes: ["conversation-db-data:/var/lib/postgresql/data"]
healthcheck: {test: ["CMD-SHELL", "pg_isready -U enkly"], interval: 5s, timeout: 5s, retries: 10}
knowledge-db:
image: pgvector/pgvector:pg16
environment: {POSTGRES_USER: enkly, POSTGRES_PASSWORD: enkly, POSTGRES_DB: enkly_knowledge}
volumes: ["knowledge-db-data:/var/lib/postgresql/data"]
healthcheck: {test: ["CMD-SHELL", "pg_isready -U enkly"], interval: 5s, timeout: 5s, retries: 10}
# ---------------------------------------------------------------------
# Application services
# ---------------------------------------------------------------------
iam:
build: {context: ., dockerfile: services/iam/Dockerfile}
depends_on:
iam-db: {condition: service_healthy}
rabbitmq: {condition: service_healthy}
environment:
ENKLY_IAM_GRPC_ADDR: ":9001"
ENKLY_IAM_POSTGRES_DSN: "postgres://enkly:enkly@iam-db:5432/enkly_iam?sslmode=disable"
ENKLY_RABBITMQ_URL: "amqp://enkly:enkly@rabbitmq:5672/"
ENKLY_BASE_URL: "${ENKLY_BASE_URL:-http://localhost:8080}"
ENKLY_SMTP_USERNAME: "${ENKLY_SMTP_USERNAME:-}"
ENKLY_SMTP_PASSWORD: "${ENKLY_SMTP_PASSWORD:-}"
restart: unless-stopped
billing:
build: {context: ., dockerfile: services/billing/Dockerfile}
depends_on:
billing-db: {condition: service_healthy}
rabbitmq: {condition: service_healthy}
environment:
ENKLY_BILLING_GRPC_ADDR: ":9002"
ENKLY_BILLING_POSTGRES_DSN: "postgres://enkly:enkly@billing-db:5432/enkly_billing?sslmode=disable"
ENKLY_RABBITMQ_URL: "amqp://enkly:enkly@rabbitmq:5672/"
STRIPE_SECRET_KEY: "${STRIPE_SECRET_KEY:-}"
STRIPE_PUBLISH_KEY: "${STRIPE_PUBLISH_KEY:-}"
STRIPE_WEBHOOK_SECRET: "${STRIPE_WEBHOOK_SECRET:-}"
STRIPE_PRICE_BASIC: "${STRIPE_PRICE_BASIC:-}"
STRIPE_PRICE_PRO: "${STRIPE_PRICE_PRO:-}"
STRIPE_PRICE_PREMIUM: "${STRIPE_PRICE_PREMIUM:-}"
restart: unless-stopped
conversation:
build: {context: ., dockerfile: services/conversation/Dockerfile}
depends_on:
conversation-db: {condition: service_healthy}
rabbitmq: {condition: service_healthy}
environment:
ENKLY_CONVERSATION_GRPC_ADDR: ":9003"
ENKLY_CONVERSATION_POSTGRES_DSN: "postgres://enkly:enkly@conversation-db:5432/enkly_conversation?sslmode=disable"
ENKLY_RABBITMQ_URL: "amqp://enkly:enkly@rabbitmq:5672/"
restart: unless-stopped
workspace:
build: {context: ., dockerfile: services/workspace/Dockerfile}
volumes: ["workspace-data:/data/workspace"]
environment:
ENKLY_WORKSPACE_GRPC_ADDR: ":9004"
ENKLY_WORKSPACE: "/data/workspace"
ENKLY_BASH_TIMEOUT_SECONDS: "${ENKLY_BASH_TIMEOUT_SECONDS:-0}"
ENKLY_BASH_ALLOWLIST: "${ENKLY_BASH_ALLOWLIST:-}"
restart: unless-stopped
knowledge:
build: {context: ., dockerfile: services/knowledge/Dockerfile}
depends_on:
knowledge-db: {condition: service_healthy}
vllm-embed: {condition: service_healthy}
environment:
ENKLY_KNOWLEDGE_GRPC_ADDR: ":9005"
ENKLY_KNOWLEDGE_POSTGRES_DSN: "postgres://enkly:enkly@knowledge-db:5432/enkly_knowledge?sslmode=disable"
ENKLY_VLLM_EMBED_URL: "https://vllm-embed.enkly.org/v1"
ENKLY_EMBED_MODEL: "${ENKLY_EMBED_MODEL:-nomic-embed-text}"
ENKLY_VLLM_API_KEY: "${ENKLY_VLLM_API_KEY:-}"
restart: unless-stopped
agent:
build: {context: ., dockerfile: services/agent/Dockerfile}
depends_on:
workspace: {condition: service_started}
knowledge: {condition: service_started}
billing: {condition: service_started}
conversation: {condition: service_started}
vllm-chat: {condition: service_healthy}
vllm-embed: {condition: service_healthy}
environment:
ENKLY_AGENT_GRPC_ADDR: ":9006"
ENKLY_WORKSPACE_GRPC_ADDR: "workspace:9004"
ENKLY_KNOWLEDGE_GRPC_ADDR: "knowledge:9005"
ENKLY_BILLING_GRPC_ADDR: "billing:9002"
ENKLY_CONVERSATION_GRPC_ADDR: "conversation:9003"
ENKLY_VLLM_CHAT_URL: "https://vllm-chat.enkly.org/v1"
ENKLY_VLLM_EMBED_URL: "https://vllm-embed.enkly.org/v1"
ENKLY_CHAT_MODEL: "${ENKLY_CHAT_MODEL:-qwen3.5}"
ENKLY_EMBED_MODEL: "${ENKLY_EMBED_MODEL:-nomic-embed-text}"
ENKLY_VLLM_API_KEY: "${ENKLY_VLLM_API_KEY:-}"
restart: unless-stopped
notification:
build: {context: ., dockerfile: services/notification/Dockerfile}
depends_on:
rabbitmq: {condition: service_healthy}
environment:
ENKLY_RABBITMQ_URL: "amqp://enkly:enkly@rabbitmq:5672/"
ENKLY_SMTP_HOST: "${ENKLY_SMTP_HOST:-smtp.gmail.com}"
ENKLY_SMTP_PORT: "${ENKLY_SMTP_PORT:-587}"
ENKLY_SMTP_USERNAME: "${ENKLY_SMTP_USERNAME:-}"
ENKLY_SMTP_PASSWORD: "${ENKLY_SMTP_PASSWORD:-}"
ENKLY_SMTP_FROM: "${ENKLY_SMTP_FROM:-}"
restart: unless-stopped
gateway:
build: {context: ., dockerfile: services/gateway/Dockerfile}
depends_on: [iam, billing, conversation, workspace, knowledge, agent]
ports: ["8080:8080"]
environment:
ENKLY_GATEWAY_HTTP_ADDR: ":8080"
ENKLY_BASE_URL: "${ENKLY_BASE_URL:-http://localhost:8080}"
ENKLY_IAM_GRPC_ADDR: "iam:9001"
ENKLY_BILLING_GRPC_ADDR: "billing:9002"
ENKLY_CONVERSATION_GRPC_ADDR: "conversation:9003"
ENKLY_WORKSPACE_GRPC_ADDR: "workspace:9004"
ENKLY_KNOWLEDGE_GRPC_ADDR: "knowledge:9005"
ENKLY_AGENT_GRPC_ADDR: "agent:9006"
STRIPE_PUBLISH_KEY: "${STRIPE_PUBLISH_KEY:-}"
restart: unless-stopped
volumes:
hf-cache:
iam-db-data:
billing-db-data:
conversation-db-data:
knowledge-db-data:
workspace-data: