# Open WebUI v0.10+ Config Schema Reference

**v0.10+ changed the `config` table from single-JSON-blob to per-key rows.**

Old schema: `CREATE TABLE config (id INTEGER, data JSON, updated_at DATETIME)`  
New schema: `CREATE TABLE config (key TEXT PRIMARY KEY, value JSON, updated_at BIGINT)`

## v0.10+ Config Keys

Keys discovered from a v0.10.2 instance (alphabetical):

| Key | Example Value | Notes |
|-----|---------------|-------|
| `code_execution.enable` | `true` | |
| `code_execution.engine` | `"pyodide"` | |
| `direct.enable` | `true` | Direct connections |
| `models.base_models_cache` | `true` | |
| `oauth.client.timeout` | `""` | Empty = default |
| `ollama.api_configs` | `{}` | |
| `ollama.base_urls` | `["http://localhost:11434"]` | |
| `ollama.enable` | `true` | |
| `openai.api_base_urls` | `["http://host:9292/v1"]` | JSON array of URL strings |
| `openai.api_configs` | `{"0": {"name":"llama-swap","model_ids":[...]}}` | Same structure as before |
| `openai.api_keys` | `["sk-..."]` | JSON array |
| `openai.enable` | `true` | |
| `openai.model_capabilities.builtin_tools` | `false` | Per-key now — not nested |
| `openai.model_capabilities.function_calling` | `false` | |
| `openai.model_capabilities.web_search` | `true` | |
| `openai.model_ids` | `["gemma-26b","qwen36-35b-mtp"]` | Top-level model filter |
| `terminal_server.connections` | `[{"id":"deb","name":"Debian Terminal",...}]` | |
| `tool_server.connections` | `[]` | |
| `ui.enable_signup` | `false` | |
| `version` | `0` | Schema version number |

## Migration Path

If you have DB scripts that access the old `SELECT data FROM config`, update them:

### Read (old → new)
```python
# OLD (pre-v0.10) — crashes on v0.10+:
row = conn.execute('SELECT data FROM config LIMIT 1').fetchone()
config = json.loads(row[0])
model_ids = config['openai']['model_ids']

# NEW (v0.10+):
row = conn.execute("SELECT value FROM config WHERE key='openai.model_ids'").fetchone()
model_ids = json.loads(row[0])
```

### Write (old → new)
```python
# OLD:
row = conn.execute('SELECT id, data FROM config LIMIT 1').fetchone()
config = json.loads(row[1])
config['openai']['model_ids'] = new_list
conn.execute('UPDATE config SET data=? WHERE id=?', (json.dumps(config), row[0]))

# NEW: each key is its own row
conn.execute(
    "UPDATE config SET value=?, updated_at=unixepoch() WHERE key='openai.model_ids'",
    (json.dumps(new_list),)
)
```

### Batch read
```python
# Read all openai config at once
keys = conn.execute("SELECT key, value FROM config WHERE key LIKE 'openai.%'").fetchall()
config = {}
for key, val in keys:
    config[key] = json.loads(val)
# Now access config['openai.model_ids'], config['openai.api_configs'], etc.
```

## Pitfalls

- **`unixepoch()`** is used for timestamps (not `datetime('now')` like the old schema)
- **The `model` table is UNCHANGED** — same columns as before
- **All value columns are JSON text** — even booleans are the string `'true'` not the JSON `true`
- **Environment vars marked as `ConfigVar`** persist on first launch and override env vars on subsequent restarts. Set `ENABLE_PERSISTENT_CONFIG=false` to force re-read of env vars.
- **DB migration is one-way** — downgrading requires restoring a backup
