109 lines
4.0 KiB
Markdown
109 lines
4.0 KiB
Markdown
# Open WebUI Configuration via SQLite
|
|
|
|
Open WebUI v0.10+ stores persistent config as JSON key/value pairs in its SQLite database at `/app/backend/data/webui.db` (Docker volume: `/var/lib/docker/volumes/openwebui_data/_data/webui.db`).
|
|
|
|
After the first launch, Docker `ConfigVar` environment variables are **ignored** — the DB is the single source of truth. All configuration changes after initial setup must be made directly in SQLite.
|
|
|
|
## Schema
|
|
|
|
```sql
|
|
CREATE TABLE config (
|
|
"key" TEXT NOT NULL,
|
|
value JSON NOT NULL,
|
|
updated_at BIGINT,
|
|
PRIMARY KEY ("key")
|
|
);
|
|
```
|
|
|
|
## Essential config keys
|
|
|
|
### Model capabilities (vision, image generation)
|
|
|
|
```sql
|
|
-- Enable vision for all models (required for image upload in chat)
|
|
UPDATE config SET value = '{"capabilities": {"vision": true}}', updated_at = <unix_ts>
|
|
WHERE key = 'models.default_metadata';
|
|
```
|
|
|
|
Without this, image upload is disabled in Open WebUI regardless of which vision-capable models are connected.
|
|
|
|
### Image generation
|
|
|
|
```sql
|
|
-- Enable image generation feature
|
|
UPDATE config SET value = 'true', updated_at = <unix_ts> WHERE key = 'image_generation.enable';
|
|
-- Engine/model is stored in separate keys
|
|
-- Engine: openai, automatic1111, comfyui, gemini
|
|
UPDATE config SET value = '"openai"', updated_at = <unix_ts> WHERE key = 'image_generation.engine';
|
|
UPDATE config SET value = '"gemini/imagen-4.0-generate-001"', updated_at = <unix_ts> WHERE key = 'image_generation.model';
|
|
```
|
|
|
|
### Web search
|
|
|
|
```sql
|
|
-- Enable web search
|
|
UPDATE config SET value = 'true', updated_at = <unix_ts> WHERE key = 'web.search.enable';
|
|
-- Set engine: searxng, google_pse, brave, serpapi, etc.
|
|
UPDATE config SET value = '"google_pse"', updated_at = <unix_ts> WHERE key = 'web.search.engine';
|
|
```
|
|
|
|
Public SearXNG instances (searx.be, search.sapti.me, etc.) are heavily rate-limited (403/429). Google PSE is the reliable fallback but uses API quota. Self-hosted SearXNG is ideal.
|
|
|
|
### MCP tool server connections
|
|
|
|
```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 []
|
|
|
|
# Remove duplicates by port
|
|
current = [c for c in current if '<port>' not in c.get('url', '')]
|
|
|
|
current.append({
|
|
'url': 'http://host.docker.internal:<PORT>/mcp',
|
|
'type': 'mcp', # CRITICAL: not 'openapi'
|
|
'auth_type': 'none',
|
|
'key': '',
|
|
'headers': None,
|
|
'config': {
|
|
'enable': True,
|
|
'function_name_filter_list': '',
|
|
'access_grants': [{
|
|
'principal_type': 'group',
|
|
'principal_id': '<group-id>', # from DB: SELECT value FROM config WHERE key='ui.default_group_id'
|
|
'permission': 'read'
|
|
}]
|
|
},
|
|
'info': {'id': '', 'name': 'Server Name', 'description': 'What it does'}
|
|
})
|
|
|
|
db.execute("UPDATE config SET value = ?, updated_at = ? WHERE key = ?",
|
|
(json.dumps(current), int(time.time()), 'tool_server.connections'))
|
|
db.commit()
|
|
db.close()
|
|
# Then: docker restart openwebui
|
|
```
|
|
|
|
### User features/permissions
|
|
|
|
```sql
|
|
-- Check image_generation and other feature flags
|
|
SELECT value FROM config WHERE key = 'user.permissions';
|
|
-- Look for JSON path: $.features.image_generation
|
|
```
|
|
|
|
## Change workflow
|
|
|
|
1. Update the DB: `python3 -c "..."` (sqlite3 write)
|
|
2. Verify: `SELECT value FROM config WHERE key = '...'`
|
|
3. Restart: `docker restart openwebui`
|
|
4. Wait 10-15 seconds for health check
|
|
|
|
## Pitfalls
|
|
|
|
- **ConfigVar env vars are dead on restart** — After first launch, `DEFAULT_MODEL_METADATA` and other ConfigVar env vars in docker-compose are silently ignored. The DB wins.
|
|
- **Quoting in shell** — When running Python SQLite updates through SSH, use single-quoted Python strings and double-quoted SQL identifiers to avoid escaping hell. Better: write a .py script, scp it, run it.
|
|
- **Group ID discovery** — The principal_id for access grants is found at `SELECT value FROM config WHERE key='ui.default_group_id'`. This changes per installation.
|