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} # 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 (IONOS SMTP) SMTP_HOST: ${EMAIL_SMTP_HOST} SMTP_PORT: ${EMAIL_SMTP_PORT} SMTP_USER: ${EMAIL_SMTP_USER} SMTP_PASSWORD: ${EMAIL_SMTP_PASSWORD} SMTP_FROM_EMAIL: ${EMAIL_FROM} SMTP_USE_TLS: false SMTP_USE_SSL: true volumes: - ai_webui_data:/app/backend/data 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 LITELLM_DROP_PARAMS: "true" volumes: - ./litellm-config.yaml:/app/litellm-config.yaml:ro command: ["--config", "/app/litellm-config.yaml", "--host", "0.0.0.0", "--port", "4000", "--detailed_debug", "--drop_params"] 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: image: ${AI_FACEFUSION_IMAGE:-facefusion/facefusion:3.5.0-cpu} container_name: ${AI_COMPOSE_PROJECT_NAME}_facefusion restart: unless-stopped command: ["python", "facefusion.py", "run", "--execution-providers", "cpu"] environment: TZ: ${TIMEZONE:-Europe/Berlin} # Force CPU execution on VPS (no GPU available yet) FACEFUSION_EXECUTION_PROVIDERS: ${AI_FACEFUSION_EXECUTION_PROVIDERS:-cpu} volumes: - ai_facefusion_data:/workspace networks: - compose_network labels: - 'traefik.enable=${AI_FACEFUSION_TRAEFIK_ENABLED}' # HTTP Basic Auth middleware - 'traefik.http.middlewares.${AI_COMPOSE_PROJECT_NAME}-facefusion-auth.basicauth.users=${AUTH_USERS}' # 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 auth - '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-auth,${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure-compress,security-headers@file' # Service - 'traefik.http.services.${AI_COMPOSE_PROJECT_NAME}-facefusion-web-secure.loadbalancer.server.port=7865' - '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