name: vexa_dev services: api-gateway: build: context: . dockerfile: services/api-gateway/Dockerfile ports: - "${API_GATEWAY_HOST_PORT:-8056}:8000" environment: - ADMIN_API_URL=http://admin-api:8001 - BOT_MANAGER_URL=http://bot-manager:8080 - TRANSCRIPTION_COLLECTOR_URL=http://transcription-collector:8000 - MCP_URL=http://mcp:18888 - REDIS_URL=redis://redis:6379/0 - PUBLIC_BASE_URL=https://share.dev.vexa.ai - TRANSCRIPT_SHARE_TTL_SECONDS=900 - LOG_LEVEL=DEBUG init: true depends_on: admin-api: condition: service_started bot-manager: condition: service_started transcription-collector: condition: service_started networks: - vexa_default restart: unless-stopped admin-api: build: context: . dockerfile: services/admin-api/Dockerfile ports: - "${ADMIN_API_HOST_PORT:-8057}:8001" environment: - DB_HOST=${DB_HOST:-postgres} - DB_PORT=${DB_PORT:-5432} - DB_NAME=${DB_NAME:-vexa} - DB_USER=${DB_USER:-postgres} - DB_PASSWORD=${DB_PASSWORD:-postgres} - DB_SSL_MODE=${DB_SSL_MODE:-disable} - ADMIN_API_TOKEN=${ADMIN_API_TOKEN} - LOG_LEVEL=DEBUG init: true networks: - vexa_default restart: unless-stopped bot-manager: build: context: . dockerfile: services/bot-manager/Dockerfile environment: - REDIS_URL=redis://redis:6379/0 - BOT_IMAGE_NAME=${BOT_IMAGE_NAME:-vexa-bot:dev} - TTS_SERVICE_URL=http://tts-service:8002 - DOCKER_NETWORK=${COMPOSE_PROJECT_NAME:-vexa}_vexa_default - LOG_LEVEL=DEBUG - DB_HOST=${DB_HOST:-postgres} - DB_PORT=${DB_PORT:-5432} - DB_NAME=${DB_NAME:-vexa} - DB_USER=${DB_USER:-postgres} - DB_PASSWORD=${DB_PASSWORD:-postgres} - DB_SSL_MODE=${DB_SSL_MODE:-disable} - DOCKER_HOST=unix://var/run/docker.sock - DEVICE_TYPE=remote - WHISPER_LIVE_URL=ws://whisperlive:9090/ws - ADMIN_TOKEN=${ADMIN_API_TOKEN} - ZOOM_CLIENT_ID=${ZOOM_CLIENT_ID} - ZOOM_CLIENT_SECRET=${ZOOM_CLIENT_SECRET} # Storage (recordings) - STORAGE_BACKEND=${STORAGE_BACKEND:-minio} - MINIO_ENDPOINT=${MINIO_ENDPOINT:-minio:9000} - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY:-vexa-access-key} - MINIO_SECRET_KEY=${MINIO_SECRET_KEY:-vexa-secret-key} - MINIO_BUCKET=${MINIO_BUCKET:-vexa-recordings} - MINIO_SECURE=${MINIO_SECURE:-false} - AWS_REGION=${AWS_REGION:-us-east-1} - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-} - S3_BUCKET=${S3_BUCKET:-} - S3_ENDPOINT=${S3_ENDPOINT:-} - S3_SECURE=${S3_SECURE:-true} - LOCAL_STORAGE_DIR=${LOCAL_STORAGE_DIR:-/data/recordings} - LOCAL_STORAGE_FSYNC=${LOCAL_STORAGE_FSYNC:-true} - RECORDING_ENABLED=${RECORDING_ENABLED:-false} - CAPTURE_MODES=${CAPTURE_MODES:-audio} # Post-meeting hooks — fires to webapp billing on meeting end - POST_MEETING_HOOKS=${POST_MEETING_HOOKS:-} # Voice agent / TTS - OPENAI_API_KEY=${OPENAI_API_KEY:-} volumes: - /var/run/docker.sock:/var/run/docker.sock - ${LOCAL_STORAGE_VOLUME_SOURCE:-recordings-data}:/data/recordings init: true depends_on: redis: condition: service_started tts-service: condition: service_started networks: - vexa_default restart: unless-stopped whisperlive: build: context: . dockerfile: services/WhisperLive/Dockerfile.cpu volumes: - wl-recordings:/var/lib/wl-recordings environment: - REDIS_STREAM_URL=redis://redis:6379/0/transcription_segments - TRANSCRIPTION_COLLECTOR_URL=redis://redis:6379/0/transcription_segments - REDIS_HOST=redis - REDIS_PORT=6379 - REDIS_DB=0 - REDIS_STREAM_NAME=transcription_segments - LANGUAGE_DETECTION_SEGMENTS=${LANGUAGE_DETECTION_SEGMENTS} - VAD_FILTER_THRESHOLD=${VAD_FILTER_THRESHOLD} - MIN_AUDIO_S=${MIN_AUDIO_S:-2.0} - MIN_AUDIO_S_TIER2=${MIN_AUDIO_S_TIER2:-20.0} - MIN_TIME_BETWEEN_REQUESTS_S=${MIN_TIME_BETWEEN_REQUESTS_S:-0.5} - MIN_TIME_BETWEEN_REQUESTS_S_TIER2=${MIN_TIME_BETWEEN_REQUESTS_S_TIER2:-20.0} - WL_RECORDING_DIR=${WL_RECORDING_DIR:-/var/lib/wl-recordings} - WL_RECORDING_FLUSH_SECONDS=${WL_RECORDING_FLUSH_SECONDS:-3} - WL_RECORDING_FSYNC_SECONDS=${WL_RECORDING_FSYNC_SECONDS:-10} - WL_RECORDING_ROTATE_SECONDS=${WL_RECORDING_ROTATE_SECONDS:-20} - WL_RECORDING_ROTATE_BYTES=${WL_RECORDING_ROTATE_BYTES:-16777216} - WL_RECORDING_SNAPSHOT_SECONDS=${WL_RECORDING_SNAPSHOT_SECONDS:-20} - WL_RECORDING_UPLOAD_URL=${WL_RECORDING_UPLOAD_URL:-http://bot-manager:8080/internal/recordings/upload} - SAME_OUTPUT_THRESHOLD=${SAME_OUTPUT_THRESHOLD:-3} - SAME_OUTPUT_THRESHOLD_TIER2=${SAME_OUTPUT_THRESHOLD_TIER2:-2} - DEVICE_TYPE=remote - REMOTE_TRANSCRIBER_URL=${REMOTE_TRANSCRIBER_URL} - REMOTE_TRANSCRIBER_API_KEY=${REMOTE_TRANSCRIBER_API_KEY} - CONSUL_ENABLE=false - WL_REDIS_DISCOVERY_ENABLED=false command: >- --port 9090 --backend remote --min_audio_s ${MIN_AUDIO_S:-2.0} --min_audio_s_tier2 ${MIN_AUDIO_S_TIER2:-20.0} --min_time_between_requests_s ${MIN_TIME_BETWEEN_REQUESTS_S:-0.5} --min_time_between_requests_s_tier2 ${MIN_TIME_BETWEEN_REQUESTS_S_TIER2:-20.0} --wl_recording_dir ${WL_RECORDING_DIR:-/var/lib/wl-recordings} --wl_recording_flush_seconds ${WL_RECORDING_FLUSH_SECONDS:-3} --wl_recording_fsync_seconds ${WL_RECORDING_FSYNC_SECONDS:-10} --wl_recording_rotate_seconds ${WL_RECORDING_ROTATE_SECONDS:-20} --wl_recording_rotate_bytes ${WL_RECORDING_ROTATE_BYTES:-16777216} --wl_recording_snapshot_seconds ${WL_RECORDING_SNAPSHOT_SECONDS:-20} --same_output_threshold ${SAME_OUTPUT_THRESHOLD:-3} --same_output_threshold_tier2 ${SAME_OUTPUT_THRESHOLD_TIER2:-2} expose: - "9090" - "9091" extra_hosts: - "host.docker.internal:host-gateway" init: true depends_on: transcription-collector: condition: service_started networks: - vexa_default restart: unless-stopped transcription-collector: build: context: . dockerfile: services/transcription-collector/Dockerfile ports: - "${TRANSCRIPTION_COLLECTOR_HOST_PORT:-8123}:8000" volumes: - ./alembic.ini:/app/alembic.ini - ./libs/shared-models/alembic:/app/alembic environment: - DB_HOST=${DB_HOST:-postgres} - DB_PORT=${DB_PORT:-5432} - DB_NAME=${DB_NAME:-vexa} - DB_USER=${DB_USER:-postgres} - DB_PASSWORD=${DB_PASSWORD:-postgres} - DB_SSL_MODE=${DB_SSL_MODE:-disable} - REDIS_HOST=redis - REDIS_PORT=6379 - REDIS_STREAM_NAME=transcription_segments - REDIS_CONSUMER_GROUP=collector_group - REDIS_STREAM_READ_COUNT=10 - REDIS_STREAM_BLOCK_MS=2000 - ADMIN_TOKEN=${ADMIN_API_TOKEN} - BACKGROUND_TASK_INTERVAL=10 - IMMUTABILITY_THRESHOLD=30 - REDIS_SEGMENT_TTL=3600 - REDIS_CLEANUP_THRESHOLD=86400 - LOG_LEVEL=DEBUG - STORAGE_BACKEND=${STORAGE_BACKEND:-minio} - MINIO_ENDPOINT=${MINIO_ENDPOINT:-minio:9000} - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY:-vexa-access-key} - MINIO_SECRET_KEY=${MINIO_SECRET_KEY:-vexa-secret-key} - MINIO_BUCKET=${MINIO_BUCKET:-vexa-recordings} - MINIO_SECURE=${MINIO_SECURE:-false} - AWS_REGION=${AWS_REGION:-us-east-1} - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-} - S3_BUCKET=${S3_BUCKET:-} - S3_ENDPOINT=${S3_ENDPOINT:-} - S3_SECURE=${S3_SECURE:-true} init: true depends_on: redis: condition: service_started networks: - vexa_default restart: unless-stopped mcp: build: context: . dockerfile: services/mcp/Dockerfile ports: - "${MCP_HOST_PORT:-18888}:18888" environment: - API_GATEWAY_URL=http://api-gateway:8000 - LOG_LEVEL=DEBUG init: true depends_on: api-gateway: condition: service_started networks: - vexa_default restart: unless-stopped dashboard: build: context: ${DASHBOARD_PATH:-../Vexa-Dashboard} ports: - "${DASHBOARD_HOST_PORT:-3001}:3000" environment: - VEXA_API_URL=http://api-gateway:8000 - VEXA_ADMIN_API_KEY=${ADMIN_API_TOKEN} - VEXA_ADMIN_API_URL=http://admin-api:8001 - JWT_SECRET=${JWT_SECRET:-vexa-dev-jwt-secret} - NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3001} init: true depends_on: api-gateway: condition: service_started networks: - vexa_default restart: unless-stopped tts-service: build: context: . dockerfile: services/tts-service/Dockerfile expose: - "8002" environment: - OPENAI_API_KEY=${OPENAI_API_KEY:-} - OPENAI_BASE_URL=${OPENAI_BASE_URL:-https://api.openai.com} - TTS_API_TOKEN=${TTS_API_TOKEN:-} - LOG_LEVEL=DEBUG init: true networks: - vexa_default restart: unless-stopped redis: image: redis:7.0-alpine command: ["redis-server", "--appendonly", "yes", "--appendfsync", "everysec"] volumes: - redis-data:/data networks: - vexa_default restart: unless-stopped minio: image: minio/minio:latest command: server /data --console-address ":9001" environment: - MINIO_ROOT_USER=${MINIO_ACCESS_KEY:-vexa-access-key} - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY:-vexa-secret-key} volumes: - minio-data:/data ports: - "${MINIO_HOST_PORT:-9000}:9000" - "${MINIO_CONSOLE_HOST_PORT:-9001}:9001" networks: - vexa_default restart: unless-stopped minio-init: image: minio/mc:latest depends_on: - minio entrypoint: - /bin/sh - -c - | sleep 5 mc alias set vexa http://minio:9000 $${MINIO_ROOT_USER:-vexa-access-key} $${MINIO_ROOT_PASSWORD:-vexa-secret-key} mc mb --ignore-existing vexa/${MINIO_BUCKET:-vexa-recordings} cat > /tmp/cors.xml <<'XML' * GET HEAD * XML mc cors set vexa/${MINIO_BUCKET:-vexa-recordings} /tmp/cors.xml || echo "minio cors set unsupported in this environment; continuing" mc anonymous set download vexa/${MINIO_BUCKET:-vexa-recordings} || true exit 0 environment: - MINIO_ROOT_USER=${MINIO_ACCESS_KEY:-vexa-access-key} - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY:-vexa-secret-key} networks: - vexa_default volumes: redis-data: postgres-data: minio-data: recordings-data: wl-recordings: networks: vexa_default: driver: bridge