Healthcheck was failing because curl is not installed in the LiteLLM container, causing Traefik to mark it as unhealthy and not route traffic. Disabled healthcheck as Traefik doesn't require it for routing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
152 lines
6.5 KiB
YAML
152 lines
6.5 KiB
YAML
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
|
|
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: sk-1234
|
|
|
|
# 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}
|
|
DATABASE_URL: null
|
|
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"]
|
|
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.middlewares.${AI_COMPOSE_PROJECT_NAME}-litellm-auth.basicauth.users=${AUTH_USERS}'
|
|
- 'traefik.http.routers.${AI_COMPOSE_PROJECT_NAME}-litellm-web-secure.middlewares=${AI_COMPOSE_PROJECT_NAME}-litellm-auth,${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}'
|
|
|
|
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
|