Open WebUI is the de-facto standard web interface for local and private LLMs in 2026. It runs in Docker, integrates directly with Ollama, and provides extensive extensibility: native RAG, web search, custom tools, agent support, and multi-user management.
This guide covers v0.3.x production deployments and best practices.
Quick Start — 5 Minutes
Docker Compose Setup
version: '3.8'
services:
open-webui:
image: ghcr.io/open-webui/open-webui:latest
container_name: open-webui
ports:
- "3000:8080"
environment:
OLLAMA_API_BASE: "http://ollama:11434"
RAG_EMBEDDING_ENGINE: "ollama"
RAG_EMBEDDING_MODEL: "nomic-embed-text"
WEBUI_URL: "http://open-webui:3000"
DATABASE_URL: "sqlite:///app/backend/data/webui.db"
volumes:
- open-webui-data:/app/backend/data
networks:
- ai-stack
restart: unless-stopped
ollama:
image: ollama/ollama:latest
container_name: ollama
environment:
OLLAMA_NUM_GPU: "1"
OLLAMA_MAX_LOADED_MODELS: "3"
volumes:
- ollama-models:/root/.ollama
networks:
- ai-stack
restart: unless-stopped
volumes:
open-webui-data:
ollama-models:
networks:
ai-stack:
driver: bridge
Start with:
docker-compose up -d
Access at http://localhost:3000
Initial Setup
- Visit
http://localhost:3000 - Click "Sign Up" to create first admin account
- Enter credentials — this becomes administrator
- After login: Settings → Connections
- Verify "Ollama API URL" is correct (e.g.,
http://ollama:11434in Docker)
Pull Models
Via Open WebUI (once Ollama connected):
- Admin Panel → Models
- Click "Pull from Ollama"
- Select model (e.g.,
qwen2.5:7b,mistral) - Wait for download (5-30 min depending on model size)
Or directly in Ollama container:
docker exec ollama ollama pull qwen2.5:7b
docker exec ollama ollama pull mistral:7b
Architecture Overview
Open WebUI consists of two main components:
- Frontend (React/TypeScript): Browser-based, runs on port 3000, responsive UI
- Backend (Python/FastAPI): Single container, REST API on port 8080 (mapped to 3000)
Browser → nginx (Port 3000)
↓
FastAPI Backend
↓
+-- Ollama API (http://ollama:11434)
+-- RAG Engine (local embeddings or external)
+-- Database (SQLite or PostgreSQL)
+-- Vector Store (ChromaDB, Weaviate optional)
Data Model
User (roles: admin, user, guest)
├── Chat Sessions (1:n)
│ ├── Messages (1:n)
│ ├── Selected Models (1:n)
│ └── RAG Documents (n:m)
├── Custom Tools (1:n)
├── Model Preferences (n:m)
└── API Keys (1:n)
Configuration Details
Environment Variables (Frontend)
| Variable | Default | Description |
|---|---|---|
OLLAMA_API_BASE |
http://localhost:11434 |
Ollama endpoint (Docker: use hostname!) |
OPENAI_API_KEY |
— | Optional: OpenAI access for external models |
OPENAI_API_BASE_URL |
— | Optional: Alternative LLM API base |
RAG_EMBEDDING_ENGINE |
ollama |
ollama, openai, or azure |
RAG_EMBEDDING_MODEL |
— | E.g., nomic-embed-text, all-minilm |
WEBUI_URL |
— | External URL for links (critical for emails!) |
WEBUI_SECRET_KEY |
auto-generated | For session encryption — don't change after init |
DATABASE_URL |
sqlite:///... |
Or PostgreSQL: postgresql://user:pw@host/db |
Environment Variables (Backend)
# Security
WEBUI_SECRET_KEY="your-secret-key"
WEBUI_AUTH_TRUSTED_EMAIL_HEADER="X-Remote-User"
# File uploads
MAX_UPLOAD_SIZE_MB=100
ALLOWED_UPLOAD_EXTENSIONS="pdf,txt,md,docx,xlsx"
# RAG / Embedding
EMBEDDING_MODEL="nomic-embed-text:latest"
CHROMA_HOST="localhost"
CHROMA_PORT=8000
# OAuth (optional)
GOOGLE_CLIENT_ID="..."
GITHUB_CLIENT_SECRET="..."
# Email
SMTP_HOST="smtp.gmail.com"
SMTP_PORT=587
SMTP_FROM_EMAIL="[email protected]"
SMTP_PASSWORD="..."
RAG Pipeline — Document Search
Open WebUI integrates Retrieval-Augmented Generation natively:
Enable RAG
- Admin Panel → Settings → RAG Configuration
- Set "Embedding Engine" to "Ollama"
- Select embedding model:
nomic-embed-text:latest - Configure "RAG Relevance Threshold" (0.3-0.5 recommended)
Upload Documents
In chat:
- Click attachment icon (📎)
- Select PDF, TXT, or MD file
- Open WebUI auto-chunks and embeds
- Embeddings stored in SQLite/ChromaDB
RAG Query Flow
User Query
↓
Embedding (local model)
↓
Vector Search (sqlite-vec or ChromaDB)
↓
Retrieve top-K similar chunks (k=3-5)
↓
Append context to LLM: "Based on: [documents]"
↓
LLM responds
Advanced: External Vector DB
For production with large document corpora (>100k chunks):
services:
open-webui:
environment:
RAG_EMBEDDING_ENGINE: "ollama"
CHROMA_HOST: "chroma"
CHROMA_PORT: "8000"
depends_on:
- chroma
chroma:
image: chromadb/chroma:latest
ports:
- "8000:8000"
volumes:
- chroma-data:/chroma/data
Model Management
List Available Models
docker exec ollama ollama list
# Or via API
curl http://localhost:11434/api/tags | jq '.models[].name'
Recommended Model Setups
| Use Case | Model | Size | VRAM | Why |
|---|---|---|---|---|
| General Chat | qwen2.5:7b |
7B | 8GB | Balanced, multilingual |
| Coding | qwen2.5:14b |
14B | 16GB | Best for programming |
| Fast | phi3:4b |
4B | 4GB | Surprisingly competent |
| Reasoning | mistral:7b |
7B | 8GB | Good reasoning despite size |
| Vision | llava:13b |
13B | 12GB | Multimodal |
Configure Model Parameters
Admin Panel → Models:
- Context Window: Higher = more history (typical 2k-8k tokens)
- Temperature: 0.0 (deterministic) to 1.0 (creative)
- Top-P: Diversity sampling (0.9 standard)
- Repeat Penalty: Prevents repetitions
Production defaults:
Temperature: 0.7
Top-P: 0.9
Repeat Penalty: 1.1
Context: 4096 (or model max)
Custom Tools & Function Calling
Open WebUI v0.3+ supports custom tools (like ChatGPT Actions):
Create Tool (Admin Panel)
- Admin Panel → Tools
- Click "Create New Tool"
- Define JSON schema:
{
"id": "weather-tool",
"name": "Weather API",
"description": "Get current weather data",
"endpoint": "https://api.weather.com/current",
"method": "GET",
"parameters": {
"city": {
"type": "string",
"description": "City name"
},
"units": {
"type": "string",
"enum": ["metric", "imperial"],
"default": "metric"
}
},
"headers": {
"Authorization": "Bearer {{WEATHER_API_KEY}}"
}
}
- Save — tool is now available to LLM
Tool Invocation
LLM automatically calls tool when relevant:
User: "What's the weather in Vienna?"
LLM: [Calls Weather Tool]
Tool Response: {"temp": 18, "conditions": "cloudy"}
LLM: "In Vienna it's 18°C and cloudy."
User Management & Roles
Role System
| Role | Permissions |
|---|---|
| admin | Everything: Settings, Users, Models, Tools, API Keys |
| user | Chats and personal settings only |
| guest | Read-only access to shared chats |
Add User
# Via Admin Panel → Users → Create User
# Or via API:
curl -X POST http://localhost:3000/api/auth/users/create \
-H "Authorization: Bearer $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "secure-password",
"name": "John Doe",
"role": "user"
}'
API Key Management
For headless integration:
- Admin Panel → Settings → API Keys
- Click "Create API Key"
- Set name and expiry
- Use in requests:
curl http://localhost:3000/api/chat \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{
"model": "qwen2.5:7b",
"messages": [{"role": "user", "content": "Hello"}]
}'
REST API Reference
Chat Completions (OpenAI-compatible)
curl http://localhost:3000/api/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen2.5:7b",
"messages": [
{"role": "system", "content": "You are helpful."},
{"role": "user", "content": "Explain quantum computing"}
],
"temperature": 0.7,
"max_tokens": 500,
"stream": false
}'
Response:
{
"id": "chatcmpl-...",
"object": "text_completion",
"created": 1711000000,
"model": "qwen2.5:7b",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "A quantum computer uses..."
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 45,
"completion_tokens": 120,
"total_tokens": 165
}
}
Streaming
Set "stream": true:
curl http://localhost:3000/api/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen2.5:7b",
"messages": [{"role": "user", "content": "Hello"}],
"stream": true
}' \
| grep -oP '"content":"\K[^"]*' | tr -d '\\n'
List Models
curl http://localhost:3000/api/models | jq '.data[].id'
Example response:
{
"data": [
{"id": "qwen2.5:7b", "object": "model", "owned_by": "ollama"},
{"id": "mistral:7b", "object": "model", "owned_by": "ollama"}
]
}
Pipelines System (Advanced)
Open WebUI 0.3.x introduces "Pipelines" — customizable processing chains:
Pipeline Types
class TextProcessingPipeline:
async def process(self, data):
# Pre-processing
data = data.strip().lower()
# LLM call
response = await llm.generate(data)
# Post-processing
return response.upper()
Enable Pipelines
Admin Panel → Settings → Pipelines:
- Document RAG: Auto-index and search
- Web Search: Augment with web context
- Code Execution: Safe Python execution
- Image Analysis: For vision models
Write Custom Pipeline
# ~/.webui/pipelines/my_pipeline.py
from typing import List, Dict
class Pipeline:
async def process(self, messages: List[Dict], **kwargs) -> Dict:
user_msg = messages[-1]["content"]
if "translate" in user_msg:
result = await self.translate(user_msg)
else:
result = await self.llm(user_msg)
return {
"role": "assistant",
"content": result
}
Reload in Admin Panel → Pipelines.
Performance Optimization
1. Model Caching
docker exec ollama bash -c 'OLLAMA_MAX_LOADED_MODELS=2 ollama serve'
Effect: Save ~40% RAM with 3+ models via smart swapping.
2. Embedding Batching
For large document corpuses (>1000 chunks):
environment:
EMBEDDING_BATCH_SIZE: 32
EMBEDDING_DEVICE: "cuda"
3. Database Indexing
For SQLite with many chats:
docker exec open-webui sqlite3 /app/backend/data/webui.db << 'EOF'
CREATE INDEX IF NOT EXISTS idx_messages_chat ON messages(chat_id);
CREATE INDEX IF NOT EXISTS idx_embeddings_doc ON embeddings(document_id);
VACUUM;
EOF
4. Frontend Caching
Browser caching is automatic, but for API clients:
curl http://localhost:3000/api/models \
-H "Cache-Control: max-age=3600"
Security & Hardening
1. Reverse Proxy (nginx)
upstream webui {
server open-webui:8080;
}
server {
listen 443 ssl;
server_name webui.example.com;
ssl_certificate /etc/nginx/certs/cert.pem;
ssl_certificate_key /etc/nginx/certs/key.pem;
location / {
proxy_pass http://webui;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
2. Authentication
Use OIDC/OAuth via reverse proxy:
environment:
WEBUI_AUTH_TRUSTED_EMAIL_HEADER: "X-Remote-User"
WEBUI_AUTH_TRUSTED_NAME_HEADER: "X-Remote-Name"
3. Rate Limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location /api/chat {
limit_req zone=api burst=20;
proxy_pass http://webui;
}
4. Secrets Management
Use .env file instead of environment variables:
WEBUI_SECRET_KEY=very-long-random-key-min-32-chars
OPENAI_API_KEY=sk_...
Docker Compose:
services:
open-webui:
env_file: .env
Troubleshooting
Ollama Connection Failed
Symptom: "Failed to connect to Ollama"
Solution:
- Verify Ollama running:
docker ps | grep ollama - In Docker network: use hostname, not localhost
- Test:
docker exec open-webui curl http://ollama:11434/api/tags
RAG Not Working
Symptom: No similar documents found
Solution:
- Admin Panel → RAG Configuration
- Verify
nomic-embed-textdownloaded:docker exec ollama ollama list | grep embed - Pull if needed:
docker exec ollama ollama pull nomic-embed-text - Documents auto-re-indexed
Out of Memory
Symptom: "CUDA out of memory" or process killed
Solution:
- Reduce context: Models → Context Length = 2048
- Reduce embedding batch:
EMBEDDING_BATCH_SIZE=8 - Use smaller model:
phi3:4binstead ofqwen2.5:14b - Limit active models:
OLLAMA_MAX_LOADED_MODELS=1
Slow Responses
Symptom: >5 seconds per query
Solutions (in order):
- Check GPU:
nvidia-smiduring query - If CPU-bound: use smaller model
- Limit models:
OLLAMA_MAX_LOADED_MODELS=1 - Check network:
docker network inspect [network]
Backup & Recovery
Database Backup
# Manual backup
docker cp open-webui:/app/backend/data/webui.db ./webui.db.backup
# Daily cron
0 2 * * * docker cp open-webui:/app/backend/data/webui.db \
/backups/webui-$(date +\%Y\%m\%d).db
Models Backup (optional, large!)
docker run --rm -v ollama-models:/data -v /backup:/backup \
alpine tar czf /backup/ollama-models.tar.gz -C /data .
Recovery
# Restore database
docker cp ./webui.db.backup open-webui:/app/backend/data/webui.db
docker restart open-webui
# Restore models
docker run --rm -v ollama-models:/data -v /backup:/backup \
alpine tar xzf /backup/ollama-models.tar.gz -C /data
docker restart ollama
Integration Patterns
With n8n
n8n webhook to Open WebUI:
{
"method": "POST",
"url": "http://open-webui:3000/api/chat/completions",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer {{ $secret.WEBUI_API_KEY }}"
},
"body": {
"model": "qwen2.5:7b",
"messages": [
{"role": "user", "content": "{{ $json.input }}"}
]
}
}
With Team-Chat
Team-Chat slash command to Open WebUI:
POST http://open-webui:3000/api/chat/completions
Header: Authorization: Bearer [API_KEY]
/ask What is AI?
Python Client
import requests
client = requests.Session()
client.headers.update({
"Authorization": f"Bearer {API_KEY}"
})
response = client.post(
"http://localhost:3000/api/chat/completions",
json={
"model": "qwen2.5:7b",
"messages": [
{"role": "system", "content": "You are helpful."},
{"role": "user", "content": "Explain XYZ"}
],
"temperature": 0.7
}
)
print(response.json()["choices"][0]["message"]["content"])
Enterprise Deployments
Multi-Tenant Setup
For true multi-tenancy (isolated data):
services:
open-webui-tenant-1:
image: ghcr.io/open-webui/open-webui:latest
environment:
DATABASE_URL: postgresql://user:pw@db/webui_tenant1
open-webui-tenant-2:
image: ghcr.io/open-webui/open-webui:latest
environment:
DATABASE_URL: postgresql://user:pw@db/webui_tenant2
PostgreSQL Backend
For production (>100 users):
services:
postgres:
image: postgres:15
environment:
POSTGRES_DB: webui
POSTGRES_PASSWORD: secure-password
volumes:
- postgres-data:/var/lib/postgresql/data
open-webui:
environment:
DATABASE_URL: postgresql://postgres:secure-password@postgres:5432/webui
