# open-webui — evaluation chat frontend over the LiteLLM gateway, on esh-docker-vm. # # WHY THIS EXISTS (bake-off against lobe-chat, 2026-08-21) # Lobe's config model does not survive a fleet that changes daily: its model # list is a static env string that never auto-discovers (it rotted in BOTH # directions — missed `sec`/`sec-reasoning` going in, kept retired `char-rp` # names that 400 on click), and half its config (TTS service/model/voice) # lives in each browser's local store, so "configured" is a property of # whoever's laptop rather than of the deploy. Open WebUI is env-declarative and # auto-tracks the gateway roster. This stack stands up PARALLEL to lobe-chat # (Lobe on :3210, this on :3211); nothing about Lobe changes until/unless the # operator retires it. The June-2026 weight objection (1,825 MB vs 143 MB) is # moot here: esh-docker-vm has ~138 GB free. # # THE DEAL-SEALER — ENABLE_PERSISTENT_CONFIG=False (see .env) # Default is True, and under True env vars are read ONCE on first boot, # written to the DB, then ignored forever — so edit-env-and-bounce silently # does nothing. We set it False so the deploy is the single source of config # truth: every knob below is re-read from env on each boot, and a change made # in the Admin UI does NOT survive a restart. Verified both directions at # deploy (G1). ⚠️ Open upstream bugs touch this flag (open-webui #20830 Redis # cache — N/A, we use no Redis; #15505; #21839) — re-verify after image bumps. # # CREDENTIAL POSTURE (G6 — chosen, not inherited) # Purpose-minted LiteLLM virtual key `key_alias: open-webui-esh`, scoped to the # `all-proxy-models` access group (reaches EVERYTHING incl. paid passthroughs — # GLM/Kimi/gen-frontier) per the operator's standing posture, BUT capped with a # `max_budget` ceiling (unlike lobe-chat-esh which is uncapped). A paid call # from this UI spends real vendor credits until the cap trips. Signup is turned # OFF after the first/admin account is created, so the surface is not open- # registration on the LAN. Secrets live in the vault, never in git: # secret get esh-docker-vm/open-webui-litellm-key -> OPENAI_API_KEY # secret get esh-docker-vm/open-webui-secret-key -> WEBUI_SECRET_KEY # secret get esh-docker-vm/open-webui-admin -> lkraven admin cred state # # TTS (G4 — tts-dev owns verification; env knobs set here) # Routed DIRECT at the fleet dots gateway http://10.100.79.3:8198/v1, NOT # through LiteLLM. Rationale (agreed with tts-dev): :8198 is itself the swap- # proof switchboard — engines swap behind it — so LiteLLM would add only a hop # and a wrong `audio/mpeg` content-type stamp; direct needs no gateway key on # the audio path. Reversible: flip AUDIO_TTS_OPENAI_API_BASE_URL to the gateway # + `ext-tts` if audio spend/log visibility is ever wanted. The five AUDIO_TTS_* # vars are tts-dev's to tune (model/voice); base URL + engine are settled. name: open-webui services: open-webui: image: ghcr.io/open-webui/open-webui:${OPENWEBUI_TAG:-main} container_name: ${OPENWEBUI_CONTAINER:-open-webui} restart: unless-stopped ports: - "${OPENWEBUI_PORT:-3211}:8080" environment: # --- The deal-sealer: deploy is the single source of config truth. --- - ENABLE_PERSISTENT_CONFIG=${ENABLE_PERSISTENT_CONFIG:-False} - ENV=${OPENWEBUI_ENV:-prod} - WEBUI_NAME=${WEBUI_NAME:-PFI Open WebUI} - WEBUI_URL=${WEBUI_URL:-http://10.0.50.45:3211} # --- Auth / signup posture (G6). Signup is flipped False post-admin. --- - WEBUI_AUTH=${WEBUI_AUTH:-True} - WEBUI_SECRET_KEY=${WEBUI_SECRET_KEY:?set in .env, from the vault} - ENABLE_SIGNUP=${ENABLE_SIGNUP:-False} - ENABLE_LOGIN_FORM=${ENABLE_LOGIN_FORM:-True} - DEFAULT_USER_ROLE=${DEFAULT_USER_ROLE:-pending} # NOTE: the env var is ENABLE_API_KEYS (plural) in Open WebUI v0.11.0 # (config.py: os.getenv('ENABLE_API_KEYS','False')). The singular form is # inert — a deploy that sets ENABLE_API_KEY silently leaves key-mint 403. - ENABLE_API_KEYS=${ENABLE_API_KEYS:-True} # --- Model source: the LiteLLM gateway, no pins (G2 auto-tracks). --- - ENABLE_OPENAI_API=${ENABLE_OPENAI_API:-True} - OPENAI_API_BASE_URL=${OPENAI_API_BASE_URL:-http://10.250.50.70:4000/v1} - OPENAI_API_KEY=${OPENAI_API_KEY:?set in .env, from the vault} # Ollama is banned fleet-wide; keep the client from probing localhost:11434. - ENABLE_OLLAMA_API=${ENABLE_OLLAMA_API:-False} # --- Background task model (G5): pin it or an unpinned default hammers a # name the gateway does not serve and fills logs with 4xx. External # (OpenAI-type) models use TASK_MODEL_EXTERNAL. summarizer = gen seat # at temp 0, the right fit for auto-title/tag/query-rewrite. --- - TASK_MODEL_EXTERNAL=${TASK_MODEL_EXTERNAL:-summarizer} # --- RAG embeddings through the gateway (fleet-consistent; avoids a boot- # time HuggingFace download of the default local sentence-transformer). --- - RAG_EMBEDDING_ENGINE=${RAG_EMBEDDING_ENGINE:-openai} - RAG_EMBEDDING_MODEL=${RAG_EMBEDDING_MODEL:-qwen3-embedding} - RAG_OPENAI_API_BASE_URL=${RAG_OPENAI_API_BASE_URL:-http://10.250.50.70:4000/v1} - RAG_OPENAI_API_KEY=${OPENAI_API_KEY:?set in .env, from the vault} # --- TTS (G4): direct at the dots gateway, engine + base URL settled; # model/voice are tts-dev's to tune. Seat is LAN/WG-only, no auth. --- - AUDIO_TTS_ENGINE=${AUDIO_TTS_ENGINE:-openai} - AUDIO_TTS_OPENAI_API_BASE_URL=${AUDIO_TTS_OPENAI_API_BASE_URL:-http://irv-ml1.nh3.internal:8198/v1} - AUDIO_TTS_OPENAI_API_KEY=${AUDIO_TTS_OPENAI_API_KEY:-sk-no-auth-lan-seat} - AUDIO_TTS_MODEL=${AUDIO_TTS_MODEL:-tts-1} - AUDIO_TTS_VOICE=${AUDIO_TTS_VOICE:-nova} # --- Telemetry off. --- - ANONYMIZED_TELEMETRY=${ANONYMIZED_TELEMETRY:-False} - DO_NOT_TRACK=${DO_NOT_TRACK:-True} - SCARF_NO_ANALYTICS=${SCARF_NO_ANALYTICS:-True} volumes: - open-webui_data:/app/backend/data healthcheck: test: ["CMD-SHELL", "python3 -c \"import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:8080/health').status==200 else 1)\" || exit 1"] interval: 30s timeout: 10s retries: 5 start_period: 120s networks: - tnet labels: - homepage.group=AI - Gateways & Chat - homepage.name=Open WebUI (esh) - homepage.icon=mdi-chat-question - homepage.description=Env-declarative chat frontend over the LiteLLM gateway (eval vs Lobe) - homepage.href=http://10.0.50.45:${OPENWEBUI_PORT:-3211} volumes: open-webui_data: name: open-webui_data networks: tnet: name: traefik-net external: true