---
date: 2026-06-22
type: decision
status: done
tags: [homelab, proxmox, open-webui, omniroute, npm, ai]
---

# Open WebUI на home lab — веб-клиент «как ChatGPT» поверх OmniRoute

## Зачем
Олегу нужен удобный веб-интерфейс для работы с ИИ на домашнем сервере (не Telegram-бот, не голый CLI). Решение: **Open WebUI** — поверх уже имеющегося OmniRoute (OpenAI-совместимый шлюз, 145 моделей вкл. `cc/claude-opus-4-8` через Max).

> NB: на **клиентской** коробке Александра Open WebUI наоборот выкинут — там мозг openclaw со своим веб-UI, а железо слабое (Sandy Bridge i3). Open WebUI оправдан только на мощном home lab. См. [[../projects/dttb/proxmox-inventory]].

## Что развёрнуто (LXC 142)
- **LXC 142 `open-webui`** на Proxmox 10.0.0.250: Debian 12, unprivileged + `nesting=1,keyctl=1` (для Docker), 2 vCPU / 2 ГБ / 12 ГБ.
- rootfs на хранилище **`work`** (11.5 ТБ), НЕ `local-lvm` (тот забит на 93%).
- Статика **10.0.0.142**, DNS контейнера = **1.1.1.1** (обойти FakeIP-перехват от роутера 10.0.0.1 при pull образов и загрузке embedding-модели).
- Docker 20.10 (из Debian-репо), контейнер `ghcr.io/open-webui/open-webui:main` v0.9.6, `-p 3000:8080`, volume `open-webui`, restart=always, onboot=1.
- Подключение к OmniRoute: `OPENAI_API_BASE_URL=http://10.0.0.179:20128/v1`, `OPENAI_API_KEY=sk-omniroute` (LAN-доступ к OmniRoute **без авторизации** → ключ любой), `ENABLE_OLLAMA_API=false`.

## Публикация
- Spaceweb A-запись `chat → 176.62.183.186` (скрипт `snippets/spaceweb-dns-api.py add-a dttb.ru chat …`).
- NPM (10.0.0.195:81) proxy host **id39** `chat.dttb.ru` → `10.0.0.142:3000`, WebSockets on, Force SSL, LE cert **id129** (до 2026-09-20).
- Локально `*.dttb.ru` уже резолвится в 10.0.0.195 (hairpin-перехват DNS на роутере) → по LAN/NetBird работает сразу, без правки DNS.

## Грабли
1. **NPM v2.14 cert-create**: при `provider:letsencrypt` тело принимает только `meta:{}` (пустой). С `letsencrypt_email`/`agree`/`dns_challenge` → `400 data/meta must NOT have additional properties`. Email берётся из аккаунта NPM. (Совпало с заметкой по коробке Александра.)
2. **DNS-распространение**: после add-a Google DoH видит запись ~через 5 мин, Cloudflare держит негативный кэш до 10 мин (SOA min TTL 600). LE запрашивает авторитативные NS напрямую → серт выпускается, как только Google увидел. Проверка только через DoH (локальный `dig`/getent перехватываются роутером).
3. **Первый старт Open WebUI**: тянет embedding-модель с HuggingFace (~1 ГБ, 30 файлов) — из РФ медленно, стартап висит на «Fetching 30 files» пару минут. Это нормально; DNS 1.1.1.1 помогает. RAG-эмбеддинги можно позже переключить на OmniRoute.
4. **Open WebUI signup**: первый зарегистрированный = admin. Открытую регистрацию выключать в Admin Panel → Settings (env `ENABLE_SIGNUP` не действует — persistent config хранится в БД и перебивает env).
5. **Max-кап**: `cc/claude-opus-4-8` делит 5-часовой кап Max с german/openclaw/swarmclaw/code-server → при нагрузке 400 «out of extra usage» (транзиентно). Для тяжёлых сессий fallback `kr/claude-sonnet-4.5` (free).
6. **Ключ OmniRoute = пустой, НЕ dummy** (важно!). Open WebUI сначала показывал **0 моделей**. Причина: домашний OmniRoute с **непустым неизвестным** ключом (`sk-omniroute`) отдаёт `{"data":[]}` (скоупит по ключу→0 провайдеров), а с **отсутствующим/пустым** auth — полный каталог (LAN-доверие). Open WebUI всегда шлёт `Authorization: Bearer <key>`. Фикс: ключ коннекта = **пустая строка** (`OPENAI_API_KEYS:[""]`). German/openclaw ходят так же (без auth); найденный в их конфигах `ork_VQo75…` — это inbound-ключ openclaw, к OmniRoute-авторизации отношения не имеет (тоже даёт 0).

## Настройка Open WebUI (через API, токен = signin админом)
- **Коннект**: `POST /openai/config/update` `{ENABLE_OPENAI_API:true, OPENAI_API_BASE_URLS:["http://10.0.0.179:20128/v1"], OPENAI_API_KEYS:[""], OPENAI_API_CONFIGS:{}}`. NB: `POST /openai/config` (без `/update`) уходит в passthrough-роут → ошибка «Direct API passthrough is disabled».
- **Дефолтная модель**: `POST /api/v1/configs/models` `{"DEFAULT_MODELS":"cc/claude-opus-4-8", ...}`. Персистится в БД, переживает рестарт. Публичный `/api/config` поле `default_models` НЕ отдаёт в v0.9.6 — это норма, не индикатор.
- **Регистрация**: `ENABLE_SIGNUP=false` по умолчанию (проверять `GET /api/v1/auths/admin/config`).
- Проверка end-to-end: `POST /api/chat/completions {model:"cc/claude-opus-4-8", messages:[...], stream:false}` → ответ от Opus 4.8.
- `openapi.json` отключён (0 байт) — интроспекции нет, эндпоинты выше зафиксированы вручную.

## Способности (что «умеет»)
Open WebUI = пассивный «кабинет подумать/найти/написать», НЕ исполнитель на инфре (это German/Антошка).
- **Веб-поиск:** DuckDuckGo (без ключа). `POST /api/v1/retrieval/config/update {web:{ENABLE_WEB_SEARCH:true, WEB_SEARCH_ENGINE:"duckduckgo", ...}}`.
- **Code interpreter:** включён из коробки (Pyodide, в браузере). `GET/POST /api/v1/configs/code_execution`.
- **База знаний (RAG):** коллекция `7f60313d-add9-4f99-ad53-89e792295129` «DTTB Knowledge Base», embedding локальный (MiniLM). **193 файла**.
  - Синк: `/opt/owui-kb-sync/sync.py` (инкрементальный по md5) + `run.sh` (flock + `git pull` + sync), **cron `*/20`** на LXC 142. Vault клонирован из Gitea (`http://oleg:***@10.0.0.189:3000/oleg/knowledge-base.git`).
  - Синкаются только `projects/decisions/claude-memory/snippets` (~200), **минус** credential-файлы (vault на 1435 .md, 1152 = `notes/` шум). Пустые и дубль-контент (Open WebUI дедупит по хешу → `DUPLICATE_CONTENT`/`EMPTY_CONTENT` 400) скрипт ловит и помечает skip в манифесте, чтоб не ретраить.
- **Модель `oleg-assistant` «Ассистент Олега»** = base `cc/claude-opus-4-8` + привязанная коллекция (`meta.knowledge`). Выбираешь её → RAG включается сам, ответы с цитатами. Проверено end-to-end. (Дефолт остался plain `cc/claude-opus-4-8` для общих вопросов; KB-модель — по выбору.)

## Команды
```bash
# контейнер
sshpass -p '1qaz!QAZ' ssh root@10.0.0.250 "pct exec 142 -- docker ps"
sshpass -p '1qaz!QAZ' ssh root@10.0.0.250 "pct exec 142 -- docker logs --tail 30 open-webui"
# обновление образа
pct exec 142 -- bash -lc 'docker pull ghcr.io/open-webui/open-webui:main && docker rm -f open-webui && <docker run ...>'
```
