diff --git a/.env.example b/.env.example index 2ef2a24..7264f8f 100644 --- a/.env.example +++ b/.env.example @@ -8,10 +8,9 @@ LLAMA_GPU_LAYERS=999 # Raise toward 131072 if you need more context; see docs/research/qwen3.8-27b-quant.md # for the VRAM math at larger context sizes. LLAMA_CTX_SIZE=65536 -LLAMA_PORT=8080 # --- Open WebUI --- -WEBUI_PORT=3000 +WEBUI_PORT=8008 # Required — create an "openwebui" virtual key in LiteLLM's Admin UI first # (see docs/proxy-key-onboarding.md), then paste it here. OPENWEBUI_LITELLM_KEY= @@ -25,6 +24,7 @@ LITELLM_PORT=4000 # Required — generate real random values before first run, e.g. `openssl rand -hex 32`. # LITELLM_SALT_KEY encrypts stored data; do not change it after the first run # (existing encrypted data becomes unreadable if you do). +# Required — generate a real random value before first run, e.g. `openssl rand -hex 32`. LITELLM_MASTER_KEY= LITELLM_SALT_KEY= -LITELLM_DB_PASSWORD=litellm +LITELLM_DB_PASSWORD= diff --git a/.gitignore b/.gitignore index 4c5f206..a7afddf 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .claude/ +.env \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 2828fde..45219b0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,26 +56,35 @@ services: - qdrant-data:/qdrant/storage restart: unless-stopped networks: [ai-stack] + healthcheck: + test: ["CMD-SHELL", "bash -c 'exec 3<>/dev/tcp/localhost/6333'"] + interval: 10s + timeout: 5s + retries: 5 open-webui: image: ghcr.io/open-webui/open-webui:main container_name: open-webui depends_on: - - qdrant - - litellm + qdrant: + condition: service_healthy + litellm: + condition: service_healthy volumes: - openwebui-data:/app/backend/data + env_file: .env environment: - WEBUI_AUTH=True # Routed through the litellm proxy, not llama-server directly — see issue #15. # OPENAI_API_KEY must be a virtual key created for Open WebUI per - # docs/proxy-key-onboarding.md (name it "openwebui"), set in .env. + # docs/proxy-key-onboarding.md (name it "openwebui"), set as + # OPENWEBUI_LITELLM_KEY in .env. - OPENAI_API_BASE_URL=http://litellm:4000/v1 - - OPENAI_API_KEY=${OPENWEBUI_LITELLM_KEY:?set to the openwebui virtual key from LiteLLM's Admin UI} + - OPENAI_API_KEY=${OPENWEBUI_LITELLM_KEY} - VECTOR_DB=qdrant - QDRANT_URI=http://qdrant:6333 ports: - - "${WEBUI_PORT:-3000}:8080" + - "${WEBUI_PORT:-8008}:8080" restart: unless-stopped networks: [ai-stack] @@ -83,14 +92,18 @@ services: image: ghcr.io/berriai/litellm:main-stable container_name: litellm depends_on: - - litellm-db - - llama-server + litellm-db: + condition: service_healthy + llama-server: + condition: service_started volumes: - ./litellm-config.yaml:/app/config.yaml:ro + # LITELLM_MASTER_KEY / LITELLM_SALT_KEY come straight from .env via env_file + # (names match what litellm reads). LITELLM_SALT_KEY must not change after + # first run — see .env.example. + env_file: .env environment: - - LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY:?set a real master key in .env — see docs/proxy-key-onboarding.md} - - LITELLM_SALT_KEY=${LITELLM_SALT_KEY:?set a real salt key in .env, do not change after first run} - - DATABASE_URL=postgresql://litellm:${LITELLM_DB_PASSWORD:-litellm}@litellm-db:5432/litellm + - DATABASE_URL=postgresql://litellm:${LITELLM_DB_PASSWORD}@litellm-db:5432/litellm command: ["--config", "/app/config.yaml", "--port", "4000"] ports: # published for LAN access (proxy.ai.home) and, via NPM, proxy.ai.haylan.ch — @@ -98,18 +111,32 @@ services: - "${LITELLM_PORT:-4000}:4000" restart: unless-stopped networks: [ai-stack] + healthcheck: + test: + - CMD-SHELL + - python3 -c "import urllib.request; urllib.request.urlopen('http://localhost:4000/health/liveliness')" + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s litellm-db: image: postgres:16-alpine container_name: litellm-db + env_file: .env environment: - POSTGRES_USER=litellm - - POSTGRES_PASSWORD=${LITELLM_DB_PASSWORD:-litellm} + - POSTGRES_PASSWORD=${LITELLM_DB_PASSWORD} - POSTGRES_DB=litellm volumes: - litellm-db-data:/var/lib/postgresql/data restart: unless-stopped networks: [ai-stack] + healthcheck: + test: ["CMD-SHELL", "pg_isready -d litellm -U litellm"] + interval: 5s + timeout: 5s + retries: 10 lazytainer: image: ghcr.io/vmorganp/lazytainer:master diff --git a/scripts/generate-secrets.sh b/scripts/generate-secrets.sh new file mode 100755 index 0000000..1ec71b1 --- /dev/null +++ b/scripts/generate-secrets.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Generates random values for the secrets docker-compose.yml requires +# (LITELLM_MASTER_KEY, LITELLM_SALT_KEY, LITELLM_DB_PASSWORD) and writes +# them into .env — creating it from .env.example first if it doesn't exist. +# +# ponytail: only fills in blank values, never overwrites ones you've already +# set — safe to re-run. Re-running won't touch LITELLM_SALT_KEY once it's +# set; changing it after first run makes existing encrypted data unreadable. +set -euo pipefail +cd "$(dirname "$0")/.." + +[ -f .env ] || cp .env.example .env + +set_if_blank() { + local key="$1" value="$2" + if grep -qE "^${key}=.*[^[:space:]]" .env; then + echo "${key}: already set, skipping." + else + sed -i "s|^${key}=.*|${key}=${value}|" .env + echo "${key}: generated." + fi +} + +set_if_blank LITELLM_MASTER_KEY "$(openssl rand -hex 32)" +set_if_blank LITELLM_SALT_KEY "$(openssl rand -hex 32)" +set_if_blank LITELLM_DB_PASSWORD "$(openssl rand -hex 32)" + +echo "Done. Review .env, then set OPENWEBUI_LITELLM_KEY per docs/proxy-key-onboarding.md."