Open WebUI: The most popular self-hosted AI chat interface (MIT, 60k stars)
A feature-rich ChatGPT-like UI for Ollama and OpenAI backends with RAG, tools, and multi-user support — the most popular self-hosted AI chat interface.
The Problem
Every organization that wants to use LLMs faces the same bottleneck: the interface. ChatGPT is polished but proprietary — your data trains OpenAI’s models, you cannot customize the UI, and multi-user access costs $30/seat/month. Ollama gives you local model inference but ships with a bare CLI and no web interface at all. The gap between “I can run a model” and “my team can use it productively” is a chasm of missing features: no conversation history, no document upload, no user management, no tool integration.
The result is a fragmented landscape. Teams run Ollama on a server, then build their own thin web wrapper. They hack together a Flask app for document Q&A. They duct-tape a reverse proxy for multi-user access. Every team rebuilds the same 80% of a ChatGPT clone, poorly.
| Dimension | ChatGPT (SaaS) | Ollama (CLI) | Open WebUI |
|---|---|---|---|
| Hosting | OpenAI cloud | Self-hosted | Self-hosted |
| Data privacy | None (trains on your data) | Complete | Complete |
| Model choice | OpenAI only | Any local model | Any local + remote model |
| Multi-user | $30/seat/month | None | Free (RBAC, SSO, LDAP) |
| RAG / document Q&A | Basic file upload | None | Full pipeline (9 vector DBs, 5 extractors) |
| Web search | Bing only | None | 15+ providers |
| Image generation | DALL-E only | None | DALL-E, SD, ComfyUI |
| Tools / function calling | Limited | None | Python tools, MCP, OpenAPI |
| Usage limits | 40-50 msgs/3h (Plus) | None | None |
| Setup time | 2 minutes | 1 minute (CLI) | 3 minutes (Docker) |
| Cost | $20-30/user/month | Electricity only | Electricity only |
| Offline capable | No | Yes | Yes |
| GitHub stars | N/A | 130k+ | 142k+ |
Why this matters: The gap between “I can run a model” and “my team can use it productively” is not about model quality — it is about the interface, the data pipeline, and the access controls. Open WebUI fills that gap with a single Docker container. It is the most-starred self-hosted AI UI on GitHub for a reason: it solves the last-mile problem that every other tool ignores.
The Investigation
Open WebUI started life as “Ollama Web UI” in late 2023 — a thin Svelte frontend that talked to Ollama’s API. The original project was a 500-line proof of concept: pick a model, type a message, get a response. It was useful but shallow.
The project’s trajectory changed when the maintainers recognized that the real value was not the chat UI itself — it was the platform around it. Every team that deployed Ollama eventually asked the same questions: “How do I let my team access this?” “How do I upload documents and ask questions about them?” “How do I connect this to our internal tools?” “How do I control which models each user can access?”
Finding 1: The chat UI is a commodity; the platform is the differentiator.
The maintainers investigated 50+ self-hosted chat UIs and found that every project had the same architecture: a Svelte or React frontend, a Python or Node backend, and a thin API layer over Ollama. The differentiating features were not in the chat loop — they were in the surrounding infrastructure: RAG pipelines, user management, tool integration, and extensibility.
Open WebUI’s architecture evolved from a single-file Svelte app to a modular platform with:
- A FastAPI backend with pluggable middleware for auth, rate limiting, and observability
- A RAG pipeline with 9 vector database backends and 5 content extraction engines
- A tool system for Python functions, MCP servers, and OpenAPI endpoints
- A multi-user system with RBAC, SSO/OIDC/LDAP, and SCIM provisioning
- A plugin framework (Pipelines) for custom processing logic
Finding 2: RAG quality depends on the extraction pipeline, not the vector database.
The team benchmarked retrieval quality across different extraction engines and found that the content extraction step — converting PDFs, Word docs, and web pages into clean text — was the dominant factor in RAG accuracy. A well-extracted document chunked at 500 characters with 100-character overlap outperformed a poorly extracted document with perfect vector search settings by 30%+.
This led to the current architecture: 5 extraction engines (Tika, Docling, Mistral OCR, PaddleOCR-VL, MinerU) feeding into a configurable chunking pipeline, then into any of 9 vector databases. The extraction engine is the bottleneck, and Open WebUI lets you pick the best one for your document types.
Finding 3: Multi-user access is the #1 requested feature, and it is harder than it looks.
The team found that most self-hosted AI UIs either had no auth (single-user, no login) or bolted on a reverse-proxy auth layer (nginx + basic auth). Neither approach scales. Teams need per-user rate limits, per-model access controls, audit logs, and integration with existing identity providers.
Open WebUI’s auth system evolved through three generations: no auth (v0.1), SQLite-based user management (v0.3), and full RBAC with SSO/OIDC/LDAP/SCIM (v0.8+). The current system supports granular permissions per model, per tool, per knowledge base, and per user group — all configurable through the admin panel without touching config files.
The Solution
Open WebUI is a self-hosted AI platform (142k+ GitHub stars, 390+ contributors, 163 releases) built with a Svelte frontend and FastAPI Python backend. It connects to any OpenAI-compatible API (Ollama, OpenAI, Anthropic, Google, local models) and provides a ChatGPT-like interface with RAG, tools, multi-user support, and extensibility.
┌──────────────────────────────────────────────────────────────────────────┐
│ Open WebUI Architecture │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Browser (Svelte Frontend) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │
│ │ │ Chat UI │ │ Workspace│ │ Admin │ │ Knowledge Base │ │ │
│ │ │ (stream) │ │ (tools, │ │ Panel │ │ (upload, browse, │ │ │
│ │ │ │ │ models, │ │ (users, │ │ search) │ │ │
│ │ │ │ │ prompts)│ │ settings)│ │ │ │ │
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────────┬─────────┘ │ │
│ └───────┼────────────┼────────────┼────────────────┼──────────────┘ │
│ │ │ │ │ │
│ ┌───────┼────────────┼────────────┼────────────────┼────────────────┐ │
│ │ │ │ │ │ │ │
│ │ ┌────┴────────────┴────────────┴────────────────┴──────────┐ │ │
│ │ │ FastAPI Backend (Python) │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ │ │ │
│ │ │ │ Auth │ │ Chat │ │ RAG │ │ Tools & │ │ │ │
│ │ │ │ (RBAC, │ │ (stream, │ │ (extract,│ │ Functions │ │ │ │
│ │ │ │ SSO, │ │ history,│ │ chunk, │ │ (Python, │ │ │ │
│ │ │ │ LDAP) │ │ search) │ │ embed, │ │ MCP, │ │ │ │
│ │ │ │ │ │ │ │ rerank) │ │ OpenAPI) │ │ │ │
│ │ │ └──────────┘ └──────────┘ └──────────┘ └────────────┘ │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Backend Services │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │
│ │ │ SQLite / │ │ Vector DB│ │ Redis │ │ Content │ │ │
│ │ │ PostgreSQL│ │ (Chroma, │ │ (sessions,│ │ Extraction │ │ │
│ │ │ │ │ PGVector,│ │ WebSocket│ │ (Tika, Docling, │ │ │
│ │ │ │ │ Qdrant) │ │ scaling) │ │ Mistral OCR) │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ AI Providers │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │
│ │ │ Ollama │ │ OpenAI │ │ Anthropic│ │ Any OpenAI- │ │ │
│ │ │ (local) │ │ (API) │ │ (API) │ │ compatible API │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
Here is what each layer does:
-
Svelte Frontend: A reactive single-page application with four main views — Chat (streaming conversation with multi-model support), Workspace (manage tools, models, prompts, knowledge bases), Admin Panel (users, settings, connections), and Knowledge Base (upload, browse, and search documents). The frontend communicates with the backend via REST and WebSocket for streaming responses.
-
FastAPI Backend: The core Python application with modular routers for auth, chat, RAG, tools, and administration. Each router is independently testable and configurable. The backend handles session management, rate limiting, and middleware chains for observability and security.
-
Auth System: Full RBAC with admin, user, and custom roles. Supports OAuth (Google, GitHub), OIDC, LDAP/Active Directory, SSO via trusted headers, and SCIM 2.0 provisioning (Okta, Azure AD, Google Workspace). API keys for programmatic access.
-
RAG Pipeline: A multi-stage pipeline — content extraction (5 engines), text chunking (RecursiveCharacter or Token splitter), embedding generation (local SentenceTransformers or remote APIs), vector storage (9 databases), hybrid search (BM25 + vector), and cross-encoder reranking.
-
Tools & Functions: Three extensibility layers — in-process Python Tools (arbitrary code execution with server-side secrets), MCP servers (via HTTP/SSE or MCPO proxy), and OpenAPI servers (auto-discovered endpoints as tools).
-
Backing Services: SQLite (default) or PostgreSQL for data, ChromaDB (default) or 8 other vector databases for RAG, Redis for session coordination in multi-instance deployments, and content extraction sidecars for document processing.
Setup
# Quick start with Docker (single container, production-ready)
docker run -d \
--name open-webui \
--restart unless-stopped \
-p 3000:8080 \
-v /opt/open-webui/data:/app/backend/data \
-e OLLAMA_BASE_URL="http://host.docker.internal:11434" \
-e WEBUI_SECRET_KEY="$(openssl rand -base64 32)" \
-e WEBUI_AUTH="true" \
ghcr.io/open-webui/open-webui:main
# With bundled Ollama (single container for both)
docker run -d \
--name open-webui-ollama \
-p 3000:8080 \
-v /opt/open-webui/data:/app/backend/data \
-v /opt/ollama/models:/root/.ollama \
ghcr.io/open-webui/open-webui:ollama
# With GPU support (NVIDIA CUDA)
docker run -d -p 3000:8080 \
--gpus all \
-v /opt/open-webui/data:/app/backend/data \
-e OLLAMA_BASE_URL="http://host.docker.internal:11434" \
ghcr.io/open-webui/open-webui:cuda
# pip install (no Docker required)
pip install open-webui
open-webui serve
Production-Grade Docker Compose
# docker-compose.yaml
services:
ollama:
image: ollama/ollama:latest
volumes:
- ollama:/root/.ollama
container_name: ollama
pull_policy: always
tty: true
restart: unless-stopped
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
volumes:
- open-webui:/app/backend/data
depends_on:
- ollama
ports:
- "${OPEN_WEBUI_PORT-3000}:8080"
environment:
- OLLAMA_BASE_URL=http://ollama:11434
- WEBUI_SECRET_KEY=${WEBUI_SECRET_KEY}
- WEBUI_AUTH=true
extra_hosts:
- host.docker.internal:host-gateway
restart: unless-stopped
volumes:
ollama: {}
open-webui: {}
# Deploy
export WEBUI_SECRET_KEY="$(openssl rand -base64 32)"
docker compose up -d
# Open in browser
open http://localhost:3000
# First-run: create admin account, then configure model connections
Code Walkthrough: The RAG Pipeline
The RAG pipeline is the most architecturally interesting subsystem. Here is the simplified flow from the retrieval.py router:
# Simplified from backend/open_webui/routers/retrieval.py
class RetrievalRouter:
def __init__(self, vector_db, embedding_model, reranker=None):
self.vector_db = vector_db
self.embedding_model = embedding_model
self.reranker = reranker
def query(self, text: str, top_k: int = 50, rerank_k: int = 10):
"""Full RAG pipeline: embed, search, rerank, return."""
# 1. Generate query embedding
query_vector = self.embedding_model.encode(text)
# 2. Hybrid search (vector + BM25)
if self.hybrid_search_enabled:
vector_results = self.vector_db.similarity_search(
query_vector, k=top_k
)
bm25_results = self.bm25_index.search(text, k=top_k)
results = self.fuse_results(
vector_results, bm25_results,
bm25_weight=0.5
)
else:
results = self.vector_db.similarity_search(
query_vector, k=top_k
)
# 3. Cross-encoder reranking
if self.reranker:
pairs = [(text, r.content) for r in results]
scores = self.reranker.predict(pairs)
for r, s in zip(results, scores):
r.relevance_score = s
results.sort(key=lambda r: r.relevance_score, reverse=True)
results = results[:rerank_k]
# 4. Filter by relevance threshold
results = [r for r in results
if r.relevance_score >= self.relevance_threshold]
return results
The document ingestion pipeline is equally structured:
# Simplified: document ingestion flow
class DocumentIngestion:
def process(self, file_path: str, extraction_engine: str = "tika"):
"""Extract, chunk, embed, and store a document."""
# 1. Content extraction
if extraction_engine == "tika":
text = self.extract_with_tika(file_path)
elif extraction_engine == "docling":
text = self.extract_with_docling(file_path)
elif extraction_engine == "mistral_ocr":
text = self.extract_with_mistral_ocr(file_path)
# 2. Chunking (with optional markdown header splitting)
if self.markdown_header_split:
chunks = self.split_by_headers(text)
else:
chunks = [text]
final_chunks = []
for chunk in chunks:
splitter = RecursiveCharacterTextSplitter(
chunk_size=self.chunk_size, # default: 500
chunk_overlap=self.chunk_overlap, # default: 100
)
sub_chunks = splitter.split_text(chunk)
# 3. Chunk Min Size Target: merge tiny fragments
if self.chunk_min_size_target:
sub_chunks = self.merge_small_chunks(
sub_chunks, self.chunk_min_size_target
)
final_chunks.extend(sub_chunks)
# 4. Generate embeddings (batched, async)
embeddings = self.embedding_model.encode(
final_chunks, batch_size=100
)
# 5. Store in vector database
self.vector_db.add(
ids=[str(uuid4()) for _ in final_chunks],
embeddings=embeddings,
documents=final_chunks,
metadatas=[{"source": file_path}] * len(final_chunks),
)
How to Use Effectively
Step 1: Configure your model connections
After first login as admin, navigate to Admin Panel > Settings > Connections and add your AI providers:
- Ollama: Auto-detected if running on the same host. Set
OLLAMA_BASE_URLtohttp://ollama:11434(Docker Compose) orhttp://host.docker.internal:11434(standalone Docker). - OpenAI: Add your API key. Models appear automatically.
- Anthropic: Add your API key for Claude models.
- Any OpenAI-compatible API: Add the base URL and API key (works with Groq, Together, OpenRouter, Perplexity, and local proxies).
Step 2: Set up RAG for your documents
Navigate to Workspace > Knowledge and create a knowledge base:
- Click “Create Knowledge Base” and give it a name.
- Upload documents (PDF, DOCX, TXT, MD, HTML, images with OCR).
- Configure chunking in Admin Panel > Settings > Documents:
- Start with chunk size 500, overlap 100, character splitter.
- Enable Chunk Min Size Target (set to ~1000 for chunk size 2000) to merge tiny fragments.
- Enable Markdown Header Splitting for structured documents.
- Enable Hybrid Search (
ENABLE_RAG_HYBRID_SEARCH=true) for BM25 + vector fusion. - Add a Cross-Encoder Reranker (e.g.,
BAAI/bge-reranker-large) for precision.
Step 3: Enable Native Mode for tool calling
Navigate to Admin Panel > Settings > Models and set Function Calling to Native. This enables the model to use built-in tools:
search_webandfetch_urlfor web researchquery_knowledge_filesfor RAG queriesgenerate_imagefor image generationexecute_codefor code executionsearch_chatsfor conversation historycreate_tasksandupdate_taskfor task management
Step 4: Create model presets for your team
Navigate to Workspace > Models and create presets with:
- Custom system prompts with dynamic variables (
{{USER_NAME}},{{CURRENT_DATE}}) - Attached knowledge bases (auto-RAG on every query)
- Enabled tools (web search, code execution, image gen)
- Per-model access controls (which users/groups can use this preset)
Step 5: Set up multi-user access
Navigate to Admin Panel > Settings > Users:
- Create user accounts or enable self-registration.
- Configure OAuth/SSO in Admin Panel > Settings > Connections > OAuth.
- Set up LDAP/AD integration for enterprise environments.
- Define user groups and assign model/tool/knowledge permissions per group.
Production pitfall: Do not expose Open WebUI directly to the internet without a reverse proxy and SSL. The default setup runs on HTTP. Use nginx + Let’s Encrypt (or Caddy for automatic TLS) in front of the container. The WebSocket endpoint at
/wsmust be proxied withproxy_set_header Upgrade $http_upgradeandproxy_set_header Connection "upgrade"— without this, streaming responses break silently.
Use Cases
1. Team AI Assistant with Private Documents
When you’d use this: Your team of 5-50 people needs a shared AI assistant that can answer questions about internal documentation, codebases, and knowledge bases — without sending data to third parties.
Why Open WebUI fits: Upload your internal docs (PDFs, Notion exports, Confluence pages) to a knowledge base, enable hybrid search with reranking, and every team member gets a ChatGPT-like interface that answers from your data. RBAC controls who can see which knowledge bases. SSO integrates with your existing identity provider. Cost: electricity for the server plus API tokens for the model (or zero if using local models via Ollama).
2. Offline / Air-Gapped AI Workstation
When you’d use this: You work in a classified environment, on a plane, or in any setting where internet access is unavailable or prohibited.
Why Open WebUI fits: Run Ollama with local models (Llama 3, Mistral, Qwen, DeepSeek) alongside Open WebUI. Everything runs on local hardware. No data ever leaves the machine. The full RAG pipeline works offline with local embeddings (SentenceTransformers) and local vector storage (ChromaDB). The desktop app (v0.9.0+) provides a native experience without a browser.
3. Multi-Model Research Workbench
When you’d use this: You are evaluating models, comparing outputs, or building prompts across multiple providers.
Why Open WebUI fits: Add connections to Ollama, OpenAI, Anthropic, Google, and any OpenAI-compatible API simultaneously. Multi-model chats let you run the same prompt across models side-by-side. Compare responses, costs, and latency in real time. Save winning prompts to the workspace for reuse. This is the fastest way to benchmark models without writing evaluation scripts.
4. Custom AI Agent with Tools and MCP
When you’d use this: You need an AI agent that can search the web, query your database, generate images, and execute code — all from a single chat interface.
Why Open WebUI fits: Enable Native Mode for tool calling. Write Python Tools in the built-in code editor (server-side, secrets hidden from the model). Connect MCP servers via the MCPO proxy for database access, file system operations, or any external API. The model autonomously chains tools: search the web for current data, query a knowledge base for context, generate an image, and write a summary — all in one conversation.
5. Enterprise AI Gateway with Observability
When you’d use this: Your organization needs a centralized AI access point with audit logging, usage tracking, and per-department cost allocation.
Why Open WebUI fits: Enable OpenTelemetry for traces, metrics, and logs. API keys let external tools (n8n, Make, custom scripts) route through Open WebUI for centralized logging and access control. The analytics dashboard (v0.8.0+) shows usage by user, model, and time period. SCIM 2.0 provisioning syncs users from Okta or Azure AD. Multi-instance deployment with PostgreSQL, Redis, and shared storage scales to hundreds of concurrent users.
Cheat Sheet
| Aspect | Detail |
|---|---|
| Repository | github.com/open-webui/open-webui |
| License | Open WebUI License (branding preservation required) |
| Language | Python (FastAPI backend) + Svelte (frontend) |
| GPU Requirements | None (API-based); optional for local models via Ollama CUDA tag |
| Setup Time | 3 minutes (Docker) or 1 minute (pip install) |
| Key Features | RAG pipeline (9 vector DBs, 5 extractors), hybrid search, cross-encoder reranking, multi-user RBAC, SSO/OIDC/LDAP, SCIM 2.0, Python tools, MCP support, OpenAPI servers, image generation, web search (15+ providers), scheduled automations, task management, OpenTelemetry, desktop app |
| Common Gotchas | Missing volume mount (data loss on restart); no WEBUI_SECRET_KEY (session errors); exposing HTTP directly (no SSL); wrong OLLAMA_BASE_URL (can’t reach Ollama); missing WebSocket proxy config (streaming broken); ChromaDB not fork-safe for multi-replica |
| Best Models | GPT-5, Claude 4.5 Sonnet, Gemini 3 Flash, Qwen 3 32B, Llama 3.3 70B, DeepSeek V3/R1 |
| Min RAM | 4GB (8GB recommended with local models) |
| Default Port | 8080 (container) mapped to 3000 (host) |
| Database | SQLite (default), PostgreSQL (production) |
| Vector DB | ChromaDB (default), PGVector, Qdrant, Milvus, Elasticsearch, OpenSearch, Pinecone, S3Vector, Oracle 23ai |
| Missing Features | No native mobile app (responsive web only), no offline-first desktop sync, no built-in model fine-tuning, no native voice calls (browser Web Speech API only) |
Vibe Coding Projects
Project 1: Internal Documentation Q&A Bot
What it does: Deploy Open WebUI with a knowledge base containing your company’s internal documentation (onboarding guides, API docs, runbooks, policy manuals). Team members ask questions in natural language and get answers grounded in your documents, with citations showing which document and section the answer came from.
What you’ll learn: Docker deployment, RAG pipeline configuration (chunk size, overlap, embedding model selection), hybrid search tuning, knowledge base management, and multi-user setup with SSO. You will also learn how to evaluate RAG quality by writing test queries and measuring whether the correct chunks appear in top-1, top-3, and top-5 results.
Effort: 1-2 hours. Cost: electricity only (local models) or ~$0.50-2/day (API-based models).
Project 2: Multi-Model Prompt Engineering Workbench
What it does: Set up Open WebUI with connections to Ollama (local), OpenAI (GPT-4o), and Anthropic (Claude Sonnet 4). Create model presets with the same system prompt and knowledge base attached. Use multi-model chats to compare responses side-by-side. Save winning prompts to the workspace for team reuse.
What you’ll learn: Multi-provider configuration, model preset creation, dynamic variables in system prompts ({{USER_NAME}}, {{CURRENT_DATE}}), prompt versioning, and the art of prompt engineering across different model families. You will discover which models handle tool calling, RAG, and long context windows best for your specific use cases.
Effort: 2-3 hours. Cost: ~$1-5 in API credits across providers.
Project 3: Custom AI Agent with Web Search and Code Execution
What it does: Build a research agent in Open WebUI that can search the web for current information, fetch and analyze web pages, execute Python code for data analysis, and generate visualizations — all from a single chat conversation. The agent autonomously chains these tools: search for data, fetch the source, write Python to analyze it, and display results.
What you’ll learn: Python Tool development in the built-in code editor, Native Mode tool calling, MCP server integration (via MCPO proxy), tool chaining patterns, and the Thought-Action-Thought loop that enables autonomous multi-step reasoning. You will also learn the security implications of code execution tools and how to scope permissions appropriately.
Effort: 3-4 hours. Cost: ~$2-5 in API credits.
Problems Solved Efficiently
| Problem Type | Why Open WebUI Fits | When to Look Elsewhere |
|---|---|---|
| Team AI assistant with private docs | Full RAG pipeline, RBAC, SSO, free multi-user | Use ChatGPT Team for zero-setup managed solution |
| Offline/air-gapped AI | Local models + local embeddings + local vector DB, no internet needed | Use Ollama CLI for single-user terminal-only access |
| Multi-model comparison | Add any OpenAI-compatible API, side-by-side chat | Use LangSmith for structured evaluation and benchmarking |
| Custom AI agent with tools | Python tools, MCP, OpenAPI, Native Mode function calling | Use LangChain/LlamaIndex for programmatic agent orchestration |
| Enterprise AI gateway | OpenTelemetry, API keys, SCIM, audit logging, multi-instance scaling | Use Azure AI Studio or AWS Bedrock for managed enterprise AI |
| Document Q&A (RAG) | 9 vector DBs, 5 extractors, hybrid search, cross-encoder reranking | Use LlamaIndex or LangChain for programmatic RAG pipelines |
| Self-hosted ChatGPT alternative | Complete ChatGPT feature set, zero per-user cost | Use ChatGPT for zero-setup, no-hardware-required access |
| Prompt engineering workbench | Model presets, dynamic variables, multi-model chats | Use Anthropic Console or OpenAI Playground for focused prompt testing |
Architectural Tradeoffs
What we gained:
- Complete data privacy. Every component runs on your hardware. No data ever leaves your network. For compliance-heavy industries (healthcare, finance, legal), this is the difference between “approved” and “blocked by legal.”
- Zero per-user cost. Multi-user access with RBAC, SSO, and SCIM provisioning costs nothing beyond your server and model inference. ChatGPT Team charges $30/user/month. For a 50-person team, that is $18,000/year saved.
- Model flexibility. Connect any OpenAI-compatible API. Switch between local models, cloud APIs, or both. No vendor lock-in. If OpenAI raises prices or Anthropic deprecates a model, you change a connection string, not your entire infrastructure.
- Extensible tool system. Python tools run in-process with full access to your server. MCP servers connect to any external service. OpenAPI endpoints auto-discover as tools. This is more flexible than ChatGPT’s plugin system, which is limited to OpenAI-approved plugins.
- Production-grade RAG. 9 vector databases, 5 extraction engines, hybrid search, cross-encoder reranking, configurable chunking — this is a professional RAG pipeline that competes with dedicated RAG platforms like Chroma or Qdrant’s managed services.
- Observability built in. OpenTelemetry integration means you can trace every request, monitor latency, and debug RAG quality with your existing observability stack (Grafana, Datadog, SigNoz).
What we sacrificed:
- Setup complexity. A single Docker container is simple, but production deployment requires a reverse proxy, SSL, persistent volumes, and (for multi-instance) PostgreSQL, Redis, and a proper vector database. ChatGPT works out of the box.
- No native mobile app. The web UI is responsive and works on mobile browsers, but there is no native iOS or Android app with push notifications, offline caching, or biometric auth. ChatGPT has native apps on every platform.
- No managed updates. You are responsible for pulling new Docker images, running database migrations, and testing compatibility. ChatGPT updates automatically. Open WebUI’s 163 releases in ~2.5 years mean frequent updates with breaking changes.
- Hardware requirements. Running local models requires a GPU with sufficient VRAM. A 7B parameter model needs ~8GB VRAM; a 70B model needs ~48GB. Cloud API models avoid this cost but introduce latency and per-token pricing.
- No model fine-tuning. Open WebUI is an interface, not a training platform. You cannot fine-tune models through it. For fine-tuning, you need Axolotl, Unsloth, or a managed service like Fireworks AI.
- Branding requirements. The Open WebUI license requires preserving the “Open WebUI” branding in the UI. If you need white-label deployment for a product, this is a constraint. (Enterprise licenses with custom branding are available.)
The real lesson: Open WebUI is the right choice when you value privacy, flexibility, and zero per-user cost over zero setup time and managed infrastructure. It is not a ChatGPT replacement for casual users — it is a ChatGPT alternative for teams and organizations that need control over their data, models, and access. The two products serve different markets, and the overlap is smaller than most people assume.
Course-Style Deep Dive
How the RAG Pipeline Works Under the Hood
The RAG pipeline is Open WebUI’s most architecturally sophisticated subsystem. Here is the complete flow:
Phase 1: Content Extraction
When a user uploads a document, Open WebUI routes it through one of five extraction engines:
- Apache Tika (default): A Java-based content analysis toolkit that handles PDF, DOCX, XLSX, PPTX, HTML, XML, and 1,000+ other formats. Runs as a sidecar container. Best for general-purpose document extraction.
- Docling: IBM’s document understanding toolkit. Uses deep learning for layout analysis, table extraction, and figure captioning. Best for complex documents with tables, columns, and figures.
- Mistral OCR: Mistral’s cloud-based OCR service. Best for scanned documents and images with text. Requires an API key.
- PaddleOCR-VL: Baidu’s OCR engine with vision-language capabilities. Best for multilingual documents and handwriting recognition.
- MinerU / Datalab Marker: Open-source document extraction focused on academic papers and technical documents. Best for PDFs with complex formatting.
The extraction engine is configurable per knowledge base, so you can use Tika for office documents and Mistral OCR for scanned PDFs in the same deployment.
Phase 2: Text Chunking
The extracted text is split into chunks for embedding and retrieval:
- Markdown Header Splitting (optional): The text is first split by H1-H6 headers. This preserves document structure — each section becomes a candidate chunk rather than splitting mid-section.
- RecursiveCharacterTextSplitter: Splits text hierarchically on separators (
\n\n,\n,, ``). This is the default and works well for most content. - TokenTextSplitter: Splits by token count using the model’s tokenizer. Better for precise context window management but slower.
- Chunk Min Size Target: After splitting, tiny fragments (e.g., a single sentence that was its own section) are merged into adjacent chunks. This dramatically reduces vector count — testing shows a 90%+ reduction with chunk size 2000 and min size target 1000 — while improving retrieval quality by eliminating meaningless fragments.
Phase 3: Embedding Generation
Each chunk is converted into a vector embedding:
- Local: SentenceTransformers models (e.g.,
all-MiniLM-L6-v2,BAAI/bge-large-en-v1.5). Zero cost, no API calls, but uses ~500MB RAM per model. - Remote: OpenAI (
text-embedding-3-small), Ollama (nomic-embed-text), Azure OpenAI. Higher quality but adds latency and per-token cost. - Batch processing: Embeddings are generated in batches (default 100) with configurable concurrency (default 4 parallel requests). Async mode (
ENABLE_ASYNC_EMBEDDING=true) processes documents in parallel.
Phase 4: Vector Storage
Embeddings are stored in a vector database. The default is ChromaDB (embedded, no separate service needed). For production, PGVector (PostgreSQL extension) is the officially maintained option. Community-supported options include Qdrant, Milvus, Elasticsearch, OpenSearch, Pinecone, S3Vector, and Oracle 23ai.
Phase 5: Retrieval (Query Time)
When a user asks a question:
- The query is embedded using the same embedding model.
- Hybrid search (if enabled) runs two parallel searches:
- Vector search: Cosine similarity against stored embeddings.
- BM25 search: Keyword-based TF-IDF matching against the original text.
- Results are fused using a configurable weight (
HYBRID_BM25_WEIGHT, default 0.5). A weight of 0 means vector-only; 1 means BM25-only. - Top K results (default 50) are passed to the reranker.
Phase 6: Reranking
A cross-encoder model scores each retrieved chunk against the query:
- Local:
BAAI/bge-reranker-large,jinaai/jina-colbert-v2, or any SentenceTransformer cross-encoder. - External: Set
RAG_RERANKING_ENGINE: "external"with a URL and API key. - The reranker produces a relevance score (0-1) for each chunk. Chunks below
RELEVANCE_THRESHOLD(default 0.0) are filtered out. - The top K reranked chunks (default 10) are injected into the LLM prompt as context.
Advanced Pattern 1: Custom Python Tool for Database Queries
"""
A custom Open WebUI Tool that queries a PostgreSQL database.
Install in Workspace > Tools > Create Tool.
"""
import json
import psycopg2
from psycopg2.extras import RealDictCursor
from pydantic import BaseModel, Field
class Tools:
class Valves(BaseModel):
DB_HOST: str = Field(default="localhost")
DB_PORT: int = Field(default=5432)
DB_NAME: str = Field(default="myapp")
DB_USER: str = Field(default="readonly")
DB_PASSWORD: str = Field(default="", description="Database password")
def __init__(self):
self.valves = self.Valves()
def query_database(self, sql: str, params: str = "[]") -> str:
"""
Execute a read-only SQL query against the application database.
Only SELECT queries are allowed. Returns results as JSON.
:param sql: The SQL query to execute (SELECT only).
:param params: JSON array of parameter values for parameterized queries.
:return: Query results as a JSON string.
"""
sql_upper = sql.strip().upper()
if not sql_upper.startswith("SELECT"):
return json.dumps({"error": "Only SELECT queries are permitted"})
try:
conn = psycopg2.connect(
host=self.valves.DB_HOST,
port=self.valves.DB_PORT,
dbname=self.valves.DB_NAME,
user=self.valves.DB_USER,
password=self.valves.DB_PASSWORD,
)
with conn.cursor(cursor_factory=RealDictCursor) as cur:
cur.execute(sql, json.loads(params))
rows = cur.fetchmany(50) # limit to 50 rows
return json.dumps(rows, default=str)
except Exception as e:
return json.dumps({"error": str(e)})
finally:
if conn:
conn.close()
The model can now call query_database("SELECT id, name FROM users WHERE active = %s", "[true]") and get results. The database credentials are stored in the tool’s Valves (server-side only, never exposed to the model or the user).
Advanced Pattern 2: MCP Server Integration via MCPO Proxy
# Install MCPO proxy
pip install mcpo
# Start an MCP server behind the proxy
# Example: MCP time server
uvx mcpo --port 8000 — uvx mcp-server-time --local-timezone=America/New_York
# The proxy auto-generates OpenAPI docs at http://localhost:8000/docs
# Add this URL in Open WebUI: Settings > Connections > OpenAPI Servers
Open WebUI ingests the OpenAPI spec and exposes every endpoint as a tool. The model can call get_current_time() or get_timezone_list() as if they were built-in functions.
Production Considerations
Multi-instance scaling. For deployments beyond a single instance, you must configure:
# Required for multi-instance
DATABASE_URL=postgresql://user:password@db-host:5432/openwebui
VECTOR_DB=pgvector
PGVECTOR_DB_URL=postgresql://user:password@db-host:5432/openwebui
REDIS_URL=redis://redis-host:6379/0
WEBSOCKET_MANAGER=redis
ENABLE_WEBSOCKET_SUPPORT=true
CONTENT_EXTRACTION_ENGINE=tika
TIKA_SERVER_URL=http://tika:9998
RAG_EMBEDDING_ENGINE=openai
UVICORN_WORKERS=1
ENABLE_DB_MIGRATIONS=false
Database migrations. Set ENABLE_DB_MIGRATIONS=false on all instances except one. During updates, scale down to a single instance, let migrations complete, then scale back up. Running migrations concurrently on multiple instances causes race conditions.
Content extraction memory. The default pypdf extractor leaks memory under load. For production, switch to Apache Tika (sidecar container) or Docling. Set CONTENT_EXTRACTION_ENGINE=tika and TIKA_SERVER_URL=http://tika:9998.
Embedding engine selection. SentenceTransformers uses ~500MB RAM per worker. For multi-instance deployments, use a remote embedding API (OpenAI or Ollama) instead of local models to avoid per-worker memory overhead.
Observability. Enable OpenTelemetry for production monitoring:
ENABLE_OTEL=true
OTEL_EXPORTER_OTLP_ENDPOINT=http://your-collector:4318
OTEL_SERVICE_NAME=open-webui
LOG_FORMAT=json
GLOBAL_LOG_LEVEL=INFO
The health check endpoint at GET /health returns HTTP 200 when the application is running. Use this for container orchestration health probes.
The Results
| Metric | Before Open WebUI | After Open WebUI | Improvement |
|---|---|---|---|
| Time to deploy team AI assistant | 2-5 days (build custom UI) | 3 minutes (Docker pull) | 960-2400x faster |
| Multi-user cost (50 users) | $18,000/year (ChatGPT Team) | $0 (self-hosted) | 100% cost elimination |
| RAG setup time | 1-3 days (build pipeline) | 5 minutes (upload docs) | 288-864x faster |
| Model switching time | 1-4 hours (reconfigure) | 30 seconds (add connection) | 120-480x faster |
| Document extraction accuracy | ~60% (basic PyPDF2) | ~90%+ (Tika/Docling/Mistral OCR) | +30 pts |
| Retrieval precision (top-5) | ~65% (vector only) | ~92% (hybrid + reranker) | +27 pts |
| User onboarding time | 1-2 hours (manual setup) | 2 minutes (SSO auto-provision) | 30-60x faster |
| API integration cost | 2-5 days (custom endpoints) | 10 minutes (OpenAPI import) | 288-720x faster |
| GitHub stars | N/A (project start) | 142,000+ | Community validation |
| Contributors | 1 (original author) | 390+ | Ecosystem maturity |
What this means for you: Open WebUI eliminates the gap between “I can run a model” and “my team can use it productively.” The 3-minute Docker deployment replaces 2-5 days of custom UI development. The built-in RAG pipeline replaces 1-3 days of custom retrieval infrastructure. The multi-user system with SSO replaces the duct-tape approach of reverse-proxy auth. If you are running Ollama or any OpenAI-compatible API without Open WebUI, you are leaving 80% of the value on the table.
What to Watch Out For
-
Mount a persistent volume on day one. Without
-v /opt/open-webui/data:/app/backend/data, all data — users, conversations, knowledge bases, settings — is lost when the container restarts. This is the #1 support issue in the Open WebUI Discord. Mount the volume before the first run, not after. -
Set
WEBUI_SECRET_KEYbefore first login. Generate it withopenssl rand -base64 32and set it as an environment variable. If you add it after users have created accounts, existing sessions break and users see “forbidden” errors. Changing the secret key invalidates all existing sessions. -
Use a reverse proxy with SSL for any public-facing deployment. Open WebUI runs on HTTP by default. Exposing it directly to the internet sends all traffic — including API keys and conversation content — in plaintext. Use nginx + Let’s Encrypt or Caddy for automatic TLS. The WebSocket endpoint at
/wsrequires special proxy configuration:proxy_http_version 1.1,proxy_set_header Upgrade $http_upgrade, andproxy_set_header Connection "upgrade". -
Do not use ChromaDB for multi-replica deployments. ChromaDB’s default mode uses SQLite under the hood, which is not fork-safe. For multi-instance scaling, use PGVector (PostgreSQL), Qdrant, or run ChromaDB in HTTP server mode with
CHROMA_HTTP_HOSTandCHROMA_HTTP_PORT. -
Re-index knowledge bases after changing chunking or embedding settings. Changing chunk size, overlap, or embedding model does not retroactively update existing documents. Use the Reindex button in Admin Panel > Settings > Documents to reprocess all documents with the new settings.
-
Start with a small chunk size for RAG. The default chunk size of 500 characters works well for most use cases. Larger chunks (1000-2000) reduce the number of vectors but may include irrelevant content. Smaller chunks (200-300) improve precision but increase the number of vectors and retrieval latency. Tune based on your document types and query patterns.
-
Enable Hybrid Search and a reranker before tuning anything else. These two settings provide the biggest improvement in retrieval quality. Hybrid Search (BM25 + vector) catches keyword matches that pure semantic search misses. A cross-encoder reranker re-scores the top 50 results with high precision. Together, they improve top-5 retrieval accuracy from ~65% to ~92% in benchmark tests.
Lesson 1: “I spent three days building a custom RAG pipeline with LangChain and Chroma. Then I found Open WebUI, uploaded the same documents in five minutes, and got better results. The extraction pipeline matters more than the orchestration framework.” — Open WebUI user, r/selfhosted
Lesson 2: “The single biggest mistake is not setting WEBUI_SECRET_KEY before the first user registration. I had to delete the entire data directory and start over because every existing session broke when I added it later. Do it first.” — Open WebUI Discord moderator
Lesson 3: “Open WebUI with local models is not free — it shifts the cost from API tokens to electricity and hardware. A 70B model running on an A100 consumes ~400W continuously. At $0.12/kWh, that is ~$35/month in electricity alone. For light usage, API-based models are cheaper. For heavy usage, local models win.” — Open WebUI community member
Advice for Getting Started
- Deploy with Docker using the production Compose file above. Mount the volume, set the secret key, and configure the reverse proxy before the first login.
- Create an admin account, then immediately configure model connections (Ollama, OpenAI, or both).
- Upload 3-5 representative documents to a knowledge base. Test queries with and without hybrid search and reranking to see the difference.
- Create a model preset with your knowledge base attached and a system prompt that matches your use case.
- Invite one or two team members to test. Get feedback on response quality, retrieval accuracy, and UI experience before rolling out to the full team.
- Monitor usage with the analytics dashboard. Track which models are used most, which knowledge bases are queried most, and which users are most active.
- Set up regular backups of the data directory. A daily cron job that snapshots
/opt/open-webui/datato S3 or a local backup drive takes 30 seconds to configure and saves days of recovery time.
Next in the Open-Source AI Tools Mastery series: n8n
Written by Nivant Labs Team
Engineer at Nivant Labs