Lessons Learned — Open WebUI Build

Kritische Erkenntnisse aus dem Sprint die im Pattern-File nachzupflegen + bei Folge-Kunden mitbedenken sind.

Open WebUI Stack-Architektur

1. Open WebUI hat KEIN natives Bedrock-Support

Architektur-Philosophie: nur OpenAI-API-compatible im Core. Bedrock-Modelle muessen ueber LiteLLM-Proxy als Sidecar laufen.

Konkret im Stack:

  • LiteLLM-Container im selben Fargate-Task, port 4000
  • Open WebUI Env OPENAI_API_BASE_URLS=http://localhost:4000/v1
  • LiteLLM Master-Key in Secrets Manager, geshared zwischen open-webui + litellm
  • TaskRole bekommt Bedrock-Invoke-Permissions, LiteLLM nutzt die via IAM
  • LiteLLM-Config inline via sh-c-Heredoc (kein Mount-Volume)

Alternativen: AWS Bedrock Access Gateway (single-purpose), Bedrock Mantle (nur open-weight). Wir bleiben bei LiteLLM weil multi-provider.

2. Open WebUI hat KEIN MongoDB-Support

Im Gegensatz zu LibreChat (das nur MongoDB hat). Open WebUI nutzt Peewee ORM → SQLite, PostgreSQL, MySQL.

Fail-Pattern den wir hatten: LibreChat-Pattern-Carryover war Mongo-Sidecar im Stack. Beim ersten Boot: RuntimeError: Unrecognized or unsupported scheme: "mongodb". Komplett Mongo raus, SQLite-Default genutzt.

3. SQLite-auf-EFS-NFS ist NICHT production-tauglich

DAS ist der Killer der den Stack mehrfach hat haengen lassen.

Symptom: Browser-Request-Storm (~18 parallele /api/config-Calls beim Page-Load) + SQLite-File-Lock auf EFS-NFS (5-10ms Latency pro Lock-Op statt 50µs auf SSD) → Worker-Pool blockiert komplett → UI antwortet nicht mehr.

Workarounds die wir gemacht haben:

  • UVICORN_WORKERS=2 → zweiter Worker faengt Hang auf, eliminiert ihn nicht
  • Custom-Model mit attached MCP-Tools → weniger UI-State-Refreshes durch Tools-Toggle
  • Manuelles Stop-Task-Restart wenn’s haengt

Echter Fix (PFLICHT-Followup): RDS PostgreSQL t4g.micro Single-AZ, +14 €/Mo. Postgres macht MVCC, parallele Reads ohne Lock-Block.

4. WEBUI_SYSTEM_PROMPT existiert NICHT als Env-Var

Mein erster Versuch einen globalen System-Prompt zu setzen. Open WebUI hat keinen globalen-System-Prompt-via-Env-Var.

Korrekte Setup-Pfade:

  • Per-User-Setting via POST /api/v1/users/user/settings/update mit {"ui": {"system": "..."}}
  • Per-Modell via Custom Model ueber POST /api/v1/models/create mit params.system. Dort substituiert Open WebUI auch {{CURRENT_DATE}}, {{USER_NAME}} etc.

Empfehlung: Custom-Model-Setup, weil multi-user-tauglich. So gemacht mit Modell vf-sonnet.

5. Template-Variables {{CURRENT_DATE}} greifen nur in Custom-Model

Open WebUI substituiert {{CURRENT_DATE}}, {{CURRENT_WEEKDAY}}, {{CURRENT_TIME}}, {{USER_NAME}}, {{USER_EMAIL}}, {{USER_ROLE}} zur Request-Zeit — aber nur im Custom-Model-System-Prompt, nicht in der ui.system User-Setting (wo’s literal als String mitgeschickt wird).

6. Tool-Server URL/Path-Split-Bug bei MCP Streamable-HTTP

Open WebUI’s Tool-Server-Schema hat url + path getrennt. Bei MCP-Streamable-HTTP-Type: path wird beim Discovery-Call ignoriert, URL muss komplett sein.

Konfig die funktioniert:

{
  "url": "https://mcp-vf.agenticventures.de/mcp",
  "path": "",
  "type": "mcp",
  "auth_type": "oauth_2.1"
}

Konfig die nicht funktioniert (Open WebUI sendet POST zu / statt /mcp):

{
  "url": "https://mcp-vf.agenticventures.de",
  "path": "/mcp"
}

7. API-Keys: ENABLE_API_KEYS=true als Env-Var nicht Default

Default ist false. Wenn man programmatic die API ansprechen will: env-var setzen, Container muss neu starten, dann ist enable_api_keys in /api/config true.

Plus: Admin-Account muss erst registriert sein (mit ENABLE_INITIAL_ADMIN_SIGNUP=true durch den ersten Signup, der dann automatisch Admin wird).

8. ENABLE_OLLAMA_API=false setzen wenn kein Ollama

Default: Open WebUI versucht host.docker.internal:11434 zu erreichen. In unserem Fargate-Setup: 500-Errors auf /ollama/api/version, die ueber Lifecycle das Admin Panel kaputtmachen.

9. minHealthyPercent: 100 fuer Make-Before-Break

Default-CDK ist minHealthyPercent: 0 mit desiredCount: 1 → ECS killt alten Task vor neuem HEALTHY = ~90s Tunnel-Downtime bei jedem Deploy.

Mit minHealthyPercent: 100 + maxHealthyPercent: 200: temporaer 2 Tasks waehrend Rolling-Deploy, alter bleibt bis neuer ready ist.

10. API-Key-Auth via Authorization: Bearer sk-...

Open WebUI API-Keys haben Format sk-<32 hex chars> = 35 Zeichen total. Auth via Authorization: Bearer sk-....

Falls Key-Validation fail-t obwohl er korrekt aussieht: Container wurde inzwischen restartet, Key in DB ggf. nicht persistiert. Neuen Key erstellen.

Cloudflare-Tunnel-Setup

11. API-Token vs Connector-Token unterscheiden

  • CF-API-Token (cfut_...) — fuer Account-API-Calls (Tunnel-Create, DNS-Edit). Wir nutzen das fuer Setup.
  • Tunnel-Connector-Token (eyJhIjoi...-Base64-JSON, ~200 Zeichen) — fuer cloudflared-Daemon-Auth zum Tunnel. Wir speichern das in AWS Secrets Manager.

Beim ersten Setup: API-Token in tmp/cf-build-token parken (kein Inline-Embed in Shell weil Sandbox blockt), nach Setup Force-Delete.

12. Make-vor-DNS-Aktivierung

Tunnel + Ingress-Config + Token in Secret VOR DNS-CNAME-Anlage — sonst hat man kurz 1033-Errors.

Bedrock-Specifics

13. EU-CRIS-Profile-IDs sind Pflicht

eu.anthropic.claude-sonnet-4-6 (mit eu.-Prefix) statt anthropic.claude-sonnet-4-6. Nackte Foundation-Model-IDs ohne Region-Prefix werfen API-Errors.

In LiteLLM-Config: model: bedrock/eu.anthropic.claude-...

14. Bedrock-Model-Access pro Account aktivieren

Bedrock-Modelle sind nicht pauschal verfuegbar, jedes muss in der Bedrock-Console → Model Access explicit angehakt werden. Self-Service-Approval fuer Anthropic-Models, sofort aktiv.

VF-Stack: Sonnet 4.6 + Haiku 4.5 sind aktiv, Opus 4.7 NICHT — Marvin muss noch klicken.

15. Bedrock-EU hat KEINE Image-Generation-Models

Nur Embedding-Image-Models in Frankfurt. Nova Canvas, Stable Diffusion etc. sind us-only. Fuer DSGVO-Kunden mit Image-Gen-Wunsch: ueber Replicate-MCP, nicht Bedrock — siehe bedrock-eu-image-gen-limitation.

16. Prompt-Caching via LiteLLM

Bedrock-Claude unterstuetzt Prompt-Caching. LiteLLM-Config: supports_prompt_caching: true pro Modell + cache_control_injection_points in litellm_settings. Cached Input ist 90% billiger als regular Input. Greift erst ab ~1024 Tokens System-Prefix.

17. Bedrock-Cold-Start nach Stack-Restart

Erster Bedrock-Call nach Container-Restart kann 15-20s dauern (Connection-Pool-Aufbau). Folge-Calls sub-2s. Kein Bug — Normal.

Open WebUI Best Practices

18. Custom-Model statt nackter LiteLLM-Modell-Name

Open WebUI’s Custom-Models kann man mit:

  • System-Prompt
  • Pre-attached Tool-IDs (meta.toolIds)
  • Description (sichtbar im Dropdown)
  • Profile-Image

Statt claude-sonnet-4-6 direkt anzubieten, bauen wir vf-sonnet als Custom-Model mit allem drin. Default-Model im Stack auf vf-sonnet. User sieht „Sonnet 4.6 (VF Standard)” im Dropdown.

19. Task-Models vs Chat-Models

Open WebUI braucht ein „Task-Model” fuer interne Tasks (Chat-Title-Generation, Tag-Extraction, Search-Query-Optimization). Default nutzt das selbe wie Chat → teuer.

TASK_MODEL=claude-haiku-4-5 macht Sinn — Haiku kostet 3.3× weniger als Sonnet, fuer interne Tasks reicht’s.

20. Tool-Schemas sind der groesste Bedrock-Cost-Treiber

114 Tools × 150-300 Tokens je Tool = 17-34k Tokens NUR FUER TOOL-SCHEMAS, bei jedem Request. Mit Prompt-Caching auf 90% reduzierbar.

Plus: User kann pro Chat Tools deselektieren — bei einem „nur-Papierkram”-Chat sind die TicketPAY + M365 Tools nicht noetig.

Sandbox + Permissions

21. Inline-Embedded-API-Tokens werden geblockt

Anthropic-Sandbox blockt Shell-Calls mit API-Tokens die aus dem User-Message kommen. Loesung: User schreibt Token in AWS Secrets Manager via einen einzelnen aws secretsmanager create-secret-Call, Agent liest dann inline mit aws secretsmanager get-secret-value | curl ....

22. Destruktive Operations brauchen explizites OK

aws cloudformation delete-stack, aws secretsmanager delete-secret --force-delete-without-recovery, ecs stop-task etc. werden vom Sandbox haeufig geblockt mit „needs explicit user confirmation”. Antwort: vor jedem destruktiven Step Marvin’s „los” abwarten.

Pricing + DSGVO

23. VF-Self-Cost-Konditionen (Marvin’s Entscheidung 2026-05-12)

Vibe Factory bekommt Hosting zum Selbstkostenpreis (~65 €/Mo nach RDS-Migration) plus Bedrock 1:1 Pass-Through. Plus Sunset-Clause ist Pflicht: „diese Pilot-Konditionen gelten bis 2026-08-31, danach review”.

Anti-Pattern: Self-Cost-Setup mit unbegrenzter Bedrock-Inclusion → Cost-Risk fuer dich bei VF-Token-Spike.

24. DSGVO-konform durch Region-Lock in LiteLLM-Config

DSGVO-Compliance durchsetzen ueber LiteLLM-Whitelist: nur Bedrock-EU-CRIS-Profile in model_list. User koennen nichts anderes auswaehlen. Plus model_info.region als Display-Feld → User sieht „EU (Frankfurt)” im Dropdown neben Modell-Name.

25. Forward-User-Info-Headers fuer Per-User-MCP-Audit

ENABLE_FORWARD_USER_INFO_HEADERS=true schickt X-OpenWebUI-User-Email etc. an MCP-Server. mcp-vf-hosted nutzt das fuer Per-User-Audit-Trail (statt nur JWT-Subject-Hash).