---
name: open-webui
description: Deploy, configure, and upgrade Open WebUI on app1 — vision support, image generation, RAG, model management, and admin-ai backend integration.
version: 1.0.0
tags: [open-webui, ai, chat, vision, rag, app1, admin-ai, docker]
---

# Open WebUI

Self-hosted AI chat interface at `http://ai.itpropartner.com` running on app1 (152.53.36.131).

## Architecture

```text
User → Caddy (ai.itpropartner.com) → localhost:3000 → Open WebUI Docker
                                                          ↓
                                              admin-ai.itpropartner.com/v1 (LiteLLM)
                                                          ↓
                                              100+ models across providers
```

## Deployment

```bash
# On app1: /docker/openwebui/docker-compose.yml
services:
  openwebui:
    image: ghcr.io/open-webui/open-webui:latest
    container_name: openwebui
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:8080"
    environment:
      - OPENAI_API_BASE_URL=https://admin-ai.itpropartner.com/v1
      - OPENAI_API_KEY=<admin-ai-key>
      - WEBUI_NAME=IT Pro Partner AI
    volumes:
      - openwebui_data:/app/backend/data
volumes:
  openwebui_data:
    external: true
```

## Models

100+ models through admin-ai. Vision-capable: claude-sonnet-4-6, gemini-flash-latest, gemini-pro-latest.

## Troubleshooting: No Models Showing Up

When OpenWebUI shows an empty model list, the API connection is failing. The DB values for `openai.api_base_urls` and `openai.api_keys` override docker-compose env vars after they've been set via the Admin UI.

### Quick diagnostic

```bash
# Test connectivity from inside the container
docker exec openwebui curl -s <base_url>/v1/models -H 'Authorization: Bearer <key>' | head -c 200

# Check current DB config
sqlite3 /var/lib/docker/volumes/openwebui_data/_data/webui.db \
  "SELECT key, substr(value, 1, 120) FROM config WHERE key IN ('openai.api_base_urls', 'openai.api_keys')"
```

### Common misconfigurations

- **`http://litellm:4000/v1`** — LiteLLM runs on core (netcup), not locally on app1. `litellm` hostname does NOT resolve from app1's Docker network. Use `https://admin-ai.itpropartner.com/v1` instead.
- **Wrong/missing API key** — The key stored in `openai.api_keys` must be a valid LiteLLM key with access to `GET /v1/models`.

### Fix

```python
import sqlite3, json, time
db = sqlite3.connect('/var/lib/docker/volumes/openwebui_data/_data/webui.db')

# Fix base URL
db.execute("UPDATE config SET value = ?, updated_at = ? WHERE key = 'openai.api_base_urls'",
           (json.dumps(['https://admin-ai.itpropartner.com/v1']), int(time.time())))
db.commit()
db.close()
```

Then restart: `docker restart openwebui`

## Enabling Vision Support (CRITICAL)

Open WebUI uses `ConfigVar` environment variables — after first launch, values persist in the SQLite database and env var changes are **ignored**. Must update the DB directly:

```python
import sqlite3, json, time
db = sqlite3.connect('/var/lib/docker/volumes/openwebui_data/_data/webui.db')
db.execute("""
    UPDATE config 
    SET value = ?, updated_at = ?
    WHERE key = 'models.default_metadata'
""", ('{"capabilities": {"vision": true}}', int(time.time())))
db.commit()
db.close()
```

Then restart: `docker restart openwebui`

**Pitfall:** Setting `DEFAULT_MODEL_METADATA` in docker-compose.yml only works on FIRST launch. Subsequent restarts use the DB value. Always update both — the compose file for fresh deploys AND the DB for live instances.

## Web Search

Configured via DB config keys. Open WebUI supports multiple search backends — switch between them by updating `web.search.engine` and the corresponding credentials.

### Current setup (Google PSE)

```python
import sqlite3, time
db = sqlite3.connect('/var/lib/docker/volumes/openwebui_data/_data/webui.db')

db.execute("UPDATE config SET value = ?, updated_at = ? WHERE key = ?",
    ('true', int(time.time()), 'web.search.enable'))
db.execute("UPDATE config SET value = ?, updated_at = ? WHERE key = ?",
    ('"google_pse"', int(time.time()), 'web.search.engine'))
db.commit()
```

Google PSE keys are already in the DB:
- `web.search.google_pse_api_key`
- `web.search.google_pse_engine_id`

### Switching search engines

- `"searxng"` — uses `web.search.searxng_query_url` (public instances are rate-limited; deploy your own)
- `"google_pse"` — uses Google Programmable Search Engine (pre-configured, works reliably)
- Other backends: brave, kagi, serpapi, tavily, etc. — each needs its own API key row

### Enabling web search in chat

Users click the 🌐 toggle in the chat input to enable web search per-message. Results are injected as RAG context with inline citations.

## MCP Connectivity

Open WebUI natively supports MCP Streamable HTTP connections. Add via **Admin Settings → External Tools** (UI) or directly in the SQLite DB.

### Adding an MCP server via DB

```python
import sqlite3, json, time
db = sqlite3.connect('/var/lib/docker/volumes/openwebui_data/_data/webui.db')

row = db.execute("SELECT value FROM config WHERE key='tool_server.connections'").fetchone()
current = json.loads(row[0]) if row else []

new_conn = {
    'url': 'http://host.docker.internal:8899/mcp',  # Use host.docker.internal for Docker→host
    'type': 'mcp',                                    # NOT 'openapi'
    'auth_type': 'none',                              # 'none', 'bearer', or 'oauth_2.1'
    'key': '',
    'config': {
        'enable': True,
        'function_name_filter_list': '',              # Leave empty to expose all tools
        'access_grants': [{'principal_type': 'group', 'principal_id': '<group-id>', 'permission': 'read'}]
    },
    'info': {'name': 'My MCP Server', 'description': '...'}
}
current.append(new_conn)
db.execute("UPDATE config SET value = ?, updated_at = ? WHERE key = ?",
    (json.dumps(current), int(time.time()), 'tool_server.connections'))
db.commit()
```

Then restart: `docker restart openwebui`

### MCP vs OpenAPI connection types

- **MCP (Streamable HTTP):** Type `"mcp"` — stateful, tool-rich, follows MCP protocol
- **OpenAPI:** Type `"openapi"` — stateless REST, follows OpenAPI spec

**Pitfall:** Using `"openapi"` as the type for an MCP server URL causes an infinite loading spinner and browser console error. Always use `"mcp"` for MCP servers.

### Access Control

MCP servers are admin-only to add. Grant user access via `access_grants` — assign to a group so all users in that group get the tools.

### Docker→host addressing

When Open WebUI runs in Docker and the MCP server is on the host, use `http://host.docker.internal:<port>/mcp` instead of `localhost`.

Configured via admin-ai → Gemini Imagen:
- Engine: openai (OpenAI-compatible API)
- Model: `gemini/imagen-4.0-generate-001`
- Size: 2048x2048

Enabled in DB config: `('image_generation.enable', 'true')`

## Document Upload (RAG)

PDFs, files, and documents are processed through the built-in RAG pipeline:
- Embedding model: `sentence-transformers/all-MiniLM-L6-v2` (downloaded from HuggingFace on first use)
- Vector DB: ChromaDB (SQLite-backed, local)
- PDF extraction: page-based mode, PDF image extraction enabled
- Chunk size: 1000, overlap: 100

## DB Config Reference

Database: `/var/lib/docker/volumes/openwebui_data/_data/webui.db`
Schema: `config(key TEXT PRIMARY KEY, value JSON, updated_at BIGINT)`

Key rows to know about:
| key | purpose |
|---|---|
| `models.default_metadata` | Vision, image_gen capabilities |
| `image_generation.enable` | Image gen toggle |
| `image_generation.model` | Active image gen model |
| `openai.api_base_urls` | Backend API URL |
| `openai.api_keys` | Backend API key |
| `user.permissions` | Feature flags per role |

## Restart

```bash
cd /docker/openwebui && docker compose down && docker compose up -d
# or for quick restart:
docker restart openwebui
```

## Upgrades

```bash
cd /docker/openwebui
docker compose pull
docker compose down && docker compose up -d
# Re-apply vision metadata (config may reset on major upgrades)
```

## Pitfalls

- **ConfigVar persistence:** After first launch, env vars like `DEFAULT_MODEL_METADATA` are ignored. Always update the SQLite DB directly for running instances. Set both env AND DB for fresh deploys.
- **Container name conflict:** `docker compose down` sometimes leaves the container name reserved. Use `docker rm -f openwebui` before `docker compose up`.
- **No sqlite3 in container:** Use Python `sqlite3` module on the host, pointing at the Docker volume path.
- **Model list refresh:** Models from admin-ai are fetched at startup. If admin-ai models change, restart Open WebUI to refresh.
- **MCP type mismatch:** Using `"openapi"` connection type for an MCP server crashes the frontend with an infinite loading spinner. Always use `"mcp"`.
- **Web search engine switch:** Google PSE is pre-configured and reliable. Public SearXNG instances are rate-limited — deploy your own if you need SearXNG.
- **`http://litellm:4000/v1` won't resolve:** LiteLLM runs on core (netcup), not locally on app1. The `litellm` hostname doesn't resolve from app1's Docker network. Always use `https://admin-ai.itpropartner.com/v1` as the base URL. This is the most common cause of "no models showing up."
- **UI overrides env vars in DB:** `openai.api_base_urls` and `openai.api_keys` set via Admin UI persist in the SQLite DB and override docker-compose environment variables. When models disappear, check the DB values first, not the compose file.
