services: # PostgreSQL with pgvector for AI/RAG workloads ai_postgres: image: ${AI_POSTGRES_IMAGE:-pgvector/pgvector:pg16} container_name: ${AI_COMPOSE_PROJECT_NAME}_postgres restart: unless-stopped environment: TZ: ${TIMEZONE:-Europe/Berlin} POSTGRES_USER: ${AI_DB_USER} POSTGRES_PASSWORD: ${AI_DB_PASSWORD} POSTGRES_DB: ${AI_DB_NAME} POSTGRES_HOST_AUTH_METHOD: scram-sha-256 POSTGRES_INITDB_ARGS: --auth-host=scram-sha-256 volumes: - ai_postgres_data:/var/lib/postgresql/data - ./postgres/init:/docker-entrypoint-initdb.d healthcheck: test: ['CMD-SHELL', 'pg_isready -U ${AI_DB_USER}'] interval: 30s timeout: 10s retries: 3 start_period: 40s networks: - compose_network # Open WebUI - ChatGPT-like interface for AI models webui: image: ${AI_WEBUI_IMAGE:-ghcr.io/open-webui/open-webui:main} container_name: ${AI_COMPOSE_PROJECT_NAME}_webui restart: unless-stopped environment: TZ: ${TIMEZONE:-Europe/Berlin} # Database configuration DATABASE_URL: postgresql://${AI_DB_USER}:${AI_DB_PASSWORD}@ai_postgres:5432/${AI_DB_NAME} # OpenAI API configuration (pointing to LiteLLM proxy) OPENAI_API_BASE_URLS: http://litellm:4000 OPENAI_API_KEYS: ${AI_LITELLM_API_KEY} # Disable Ollama (we only use LiteLLM) ENABLE_OLLAMA_API: false OLLAMA_BASE_URLS: "" # WebUI configuration WEBUI_NAME: ${AI_WEBUI_NAME:-Pivoine AI} WEBUI_URL: https://${AI_TRAEFIK_HOST} WEBUI_SECRET_KEY: ${AI_WEBUI_SECRET_KEY} # Feature flags ENABLE_SIGNUP: ${AI_ENABLE_SIGNUP:-true} ENABLE_RAG_WEB_SEARCH: ${AI_ENABLE_RAG_WEB_SEARCH:-true} ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION: ${AI_ENABLE_RAG_SSL_VERIFY:-true} # RAG configuration RAG_EMBEDDING_ENGINE: ${AI_RAG_EMBEDDING_ENGINE:-openai} RAG_EMBEDDING_MODEL: ${AI_RAG_EMBEDDING_MODEL:-text-embedding-3-small} VECTOR_DB: ${AI_VECTOR_DB:-pgvector} # Email configuration (Mailpit SMTP relay) SMTP_HOST: net_mailpit SMTP_PORT: 1025 SMTP_FROM_EMAIL: ${EMAIL_FROM} SMTP_USE_TLS: false SMTP_USE_SSL: false volumes: - ai_webui_data:/app/backend/data - ./functions:/app/backend/data/functions:ro depends_on: - ai_postgres - litellm networks: - compose_network labels: - 'traefik.enable=${AI_TRAEFIK_ENABLED}' # HTTP to HTTPS redirect - 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-redirect-web-secure.redirectscheme.scheme=https' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web.middlewares=${AI_COMPOSE_PROJECT_NAME}-redirect-web-secure' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web.rule=Host(`${AI_TRAEFIK_HOST}`)' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web.entrypoints=web' # HTTPS router - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web-secure.rule=Host(`${AI_TRAEFIK_HOST}`)' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web-secure.tls.certresolver=resolver' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web-secure.entrypoints=web-secure' - 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-web-secure-compress.compress=true' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-web-secure-compress,security-headers@file' # Service - 'traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-web-secure.loadbalancer.server.port=8080' - 'traefik.docker.network=${NETWORK_NAME}' # Watchtower - 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' # LiteLLM - Proxy to convert Anthropic API to OpenAI-compatible format litellm: image: ghcr.io/berriai/litellm:main-latest container_name: ${AI_COMPOSE_PROJECT_NAME}_litellm restart: unless-stopped environment: TZ: ${TIMEZONE:-Europe/Berlin} ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} LITELLM_MASTER_KEY: ${AI_LITELLM_API_KEY} DATABASE_URL: postgresql://${AI_DB_USER}:${AI_DB_PASSWORD}@ai_postgres:5432/litellm GPU_TAILSCALE_IP: ${GPU_TAILSCALE_IP} GPU_VLLM_QWEN_URL: ${GPU_VLLM_QWEN_URL} GPU_VLLM_LLAMA_URL: ${GPU_VLLM_LLAMA_URL} # LITELLM_DROP_PARAMS: 'true' # DISABLED: Was breaking streaming NO_DOCS: 'true' NO_REDOC: 'true' # Performance optimizations LITELLM_LOG: 'DEBUG' # Enable detailed logging for debugging streaming issues LITELLM_MODE: 'PRODUCTION' # Production mode for better performance volumes: - ./litellm-config.yaml:/app/litellm-config.yaml:ro command: [ '--config', '/app/litellm-config.yaml', '--host', '0.0.0.0', '--port', '4000' ] depends_on: - ai_postgres networks: - compose_network healthcheck: disable: true labels: - 'traefik.enable=${AI_TRAEFIK_ENABLED}' # HTTP to HTTPS redirect - 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-litellm-redirect-web-secure.redirectscheme.scheme=https' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web.middlewares=${AI_COMPOSE_PROJECT_NAME}-litellm-redirect-web-secure' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web.rule=Host(`${AI_LITELLM_TRAEFIK_HOST}`)' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web.entrypoints=web' # HTTPS router - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.rule=Host(`${AI_LITELLM_TRAEFIK_HOST}`)' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.tls.certresolver=resolver' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.entrypoints=web-secure' - 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure-compress.compress=true' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure-compress,security-headers@file' # Service - 'traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.loadbalancer.server.port=4000' - 'traefik.docker.network=${NETWORK_NAME}' # Watchtower - 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' # Crawl4AI - Web scraping for LLMs (internal API, no public access) crawl4ai: image: ${AI_CRAWL4AI_IMAGE:-unclecode/crawl4ai:latest} container_name: ${AI_COMPOSE_PROJECT_NAME}_crawl4ai restart: unless-stopped environment: TZ: ${TIMEZONE:-Europe/Berlin} # API configuration PORT: ${AI_CRAWL4AI_PORT:-11235} volumes: - ai_crawl4ai_data:/app/.crawl4ai networks: - compose_network labels: # No Traefik exposure - internal only - 'traefik.enable=false' # Watchtower - 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' # Facefusion - AI face swapping and enhancement facefusion: build: context: . dockerfile: Dockerfile image: facefusion-patched:3.5.0-cpu container_name: ${AI_COMPOSE_PROJECT_NAME}_facefusion restart: unless-stopped tty: true command: ['python', '-u', 'facefusion.py', 'run'] environment: TZ: ${TIMEZONE:-Europe/Berlin} GRADIO_SERVER_NAME: "0.0.0.0" GRADIO_SERVER_PORT: "7860" volumes: - ai_facefusion_data:/workspace networks: - compose_network labels: - 'traefik.enable=${AI_FACEFUSION_TRAEFIK_ENABLED}' # HTTP to HTTPS redirect - 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-facefusion-redirect-web-secure.redirectscheme.scheme=https' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web.middlewares=${AI_COMPOSE_PROJECT_NAME}-facefusion-redirect-web-secure' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web.rule=Host(`${AI_FACEFUSION_TRAEFIK_HOST}`)' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web.entrypoints=web' # HTTPS router with Authelia - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.rule=Host(`${AI_FACEFUSION_TRAEFIK_HOST}`)' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.tls.certresolver=resolver' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.entrypoints=web-secure' - 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure-compress.compress=true' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure-compress,net-authelia,security-headers@file' # Service - 'traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.loadbalancer.server.port=7860' - 'traefik.docker.network=${NETWORK_NAME}' # Watchtower - disabled for custom local image - 'com.centurylinklabs.watchtower.enable=false' # ComfyUI - Node-based UI for Flux image generation (proxies to RunPod GPU) comfyui: image: nginx:alpine container_name: ${AI_COMPOSE_PROJECT_NAME}_comfyui restart: unless-stopped environment: TZ: ${TIMEZONE:-Europe/Berlin} COMFYUI_BACKEND_HOST: ${GPU_TAILSCALE_IP} COMFYUI_BACKEND_PORT: ${COMFYUI_BACKEND_PORT:-8188} volumes: - ./comfyui-nginx.conf:/etc/nginx/nginx.conf.template:ro command: /bin/sh -c "envsubst '$${COMFYUI_BACKEND_HOST},$${COMFYUI_BACKEND_PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf && exec nginx -g 'daemon off;'" networks: - compose_network labels: - 'traefik.enable=${AI_COMFYUI_TRAEFIK_ENABLED:-true}' # HTTP to HTTPS redirect - 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-comfyui-redirect-web-secure.redirectscheme.scheme=https' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web.middlewares=${AI_COMPOSE_PROJECT_NAME}-comfyui-redirect-web-secure' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web.rule=Host(`${AI_COMFYUI_TRAEFIK_HOST:-comfy.ai.pivoine.art}`)' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web.entrypoints=web' # HTTPS router with Authelia SSO - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure.rule=Host(`${AI_COMFYUI_TRAEFIK_HOST:-comfy.ai.pivoine.art}`)' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure.tls.certresolver=resolver' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure.entrypoints=web-secure' - 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure-compress.compress=true' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure-compress,net-authelia,security-headers@file' # Service - 'traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-comfyui-web-secure.loadbalancer.server.port=80' - 'traefik.docker.network=${NETWORK_NAME}' # Watchtower - 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' # Supervisor - Process manager web UI (proxies to RunPod GPU) supervisor: image: nginx:alpine container_name: ${AI_COMPOSE_PROJECT_NAME}_supervisor restart: unless-stopped environment: TZ: ${TIMEZONE:-Europe/Berlin} SUPERVISOR_BACKEND_HOST: ${GPU_TAILSCALE_IP} SUPERVISOR_BACKEND_PORT: ${SUPERVISOR_BACKEND_PORT:-9001} volumes: - ./supervisor-nginx.conf:/etc/nginx/nginx.conf.template:ro command: /bin/sh -c "envsubst '$${SUPERVISOR_BACKEND_HOST},$${SUPERVISOR_BACKEND_PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf && exec nginx -g 'daemon off;'" networks: - compose_network labels: - 'traefik.enable=${AI_SUPERVISOR_TRAEFIK_ENABLED:-true}' # HTTP to HTTPS redirect - 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-supervisor-redirect-web-secure.redirectscheme.scheme=https' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web.middlewares=${AI_COMPOSE_PROJECT_NAME}-supervisor-redirect-web-secure' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web.rule=Host(`${AI_SUPERVISOR_TRAEFIK_HOST:-supervisor.ai.pivoine.art}`)' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web.entrypoints=web' # HTTPS router with Authelia SSO - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure.rule=Host(`${AI_SUPERVISOR_TRAEFIK_HOST:-supervisor.ai.pivoine.art}`)' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure.tls.certresolver=resolver' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure.entrypoints=web-secure' - 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure-compress.compress=true' - 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure-compress,net-authelia,security-headers@file' # Service - 'traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-supervisor-web-secure.loadbalancer.server.port=80' - 'traefik.docker.network=${NETWORK_NAME}' # Watchtower - 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}' volumes: ai_postgres_data: name: ${AI_COMPOSE_PROJECT_NAME}_postgres_data ai_webui_data: name: ${AI_COMPOSE_PROJECT_NAME}_webui_data ai_crawl4ai_data: name: ${AI_COMPOSE_PROJECT_NAME}_crawl4ai_data ai_facefusion_data: name: ${AI_COMPOSE_PROJECT_NAME}_facefusion_data