Оригинал: https://timofei-ermolaev.ru/courses/open-webui/drugie-sposoby/zapusk-iz-ishodnogo-koda/

# Запуск из исходного кода для разработки

Разберём локальный запуск Open WebUI из исходного кода: frontend и backend, отдельные данные, адреса и порты. Внесём небольшую правку и проверим dev reload.

[![Тимофей Ермолаев](https://timofei-ermolaev.ru/_astro/timofei-ermolaev-portrait.s0mCFw-J_Z1x8STQ.webp)](<https://timofei-ermolaev.ru/ob-avtore/>)

[Тимофей Ермолаев](<https://timofei-ermolaev.ru/ob-avtore/>) Директор по информационным технологиям (CIO)

**Опубликовано**

19 октября 2026 г.

**Обновлено**

19 октября 2026 г.

**На урок**

40 мин

**Тема**

[Другие способы развёртывания](<https://timofei-ermolaev.ru/courses/open-webui/drugie-sposoby/>)

## После урока вы сможете

- Отличить разработку Open WebUI от обычной установки для чата
- Подготовить два локальных процесса на закреплённой версии исходников
- Отделить dev-данные и ключ от рабочего экземпляра
- Проверить небольшую правку интерфейса и различать UI, API и ответ модели

**Проверяемый результат:** Вы сможете подготовить отдельную локальную среду разработки Open WebUI, а при выполнении в своей разрешённой среде проверить правку интерфейса и объяснить путь запроса к backend и модели.

Допустим, вы хотите проверить небольшую собственную правку в Open WebUI: добавить к существующей подписи нового чата метку `· dev`, увидеть её в браузере, а затем вернуть исходную строку. Для такой задачи нужны закреплённые исходники, два отдельных локальных процесса и свой лабораторный экземпляр с отдельными данными и ключом; готовая языковая модель для этой проверки не требуется.

Разберём один маршрут для нативной Ubuntu 24.04 x86\_64, где в терминале Bash уже доступны нужные версии Git, Node.js, npm и Python 3.11. Команды и фрагменты кода ниже сверены с закреплёнными исходниками выпуска `v0.12.3` и проверены статически: установка пакетов, запуск серверов разработки, обновление интерфейса и работа модели при подготовке этого текста не выполнялись, поэтому все наблюдения дальше описаны как ориентиры для вашей собственной проверки.

## Что должно работать, чтобы увидеть свою правку

Зачем ради одной короткой метки `· dev` в боковой панели запускать сразу две программы и почему для этого не нужна модель? В режиме разработки интерфейс и серверная часть разделены, чтобы вы могли править файлы на диске и сразу проверять результат без полной пересборки готового пакета или контейнера.

Когда вы просто пользуетесь приложением для переписки с моделью, исходный код вам не нужен: для этого проще и надёжнее подходят [обычная установка через Docker Compose](<https://timofei-ermolaev.ru/courses/open-webui/install/docker-compose/>) или запуск пакета [Open WebUI через Python](<https://timofei-ermolaev.ru/courses/open-webui/drugie-sposoby/open-webui-python/>) . Не заменяет исходники и готовый контейнерный образ с тегом `:dev`: как отмечается в [официальном руководстве по разработке Open WebUI](<https://raw.githubusercontent.com/open-webui/docs/0b8f7318d88208257da1135675b6ab6cc9d3046d/docs/getting-started/advanced-topics/development.md>) , такой образ позволяет познакомиться с предрелизной сборкой, но не даёт редактируемого дерева исходного кода и живого обновления компонентов.

При локальной разработке из исходников роли распределяются между тремя участниками:

- **Сервер разработки интерфейса (frontend)** работает на базе SvelteKit и сборщика Vite. Он читает исходные файлы компонентов, отдаёт страницу браузеру на адресе `http://127.0.0.1:5173` и отслеживает изменения в коде интерфейса.
- **Прокси-маршрутизатор внутри Vite** принимает от браузера запросы к серверному API и WebSocket-соединениям на том же порту `5173` и перенаправляет их серверной части. В закреплённой версии [`src/lib/constants.ts`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/src/lib/constants.ts>) базовый адрес `WEBUI_BASE_URL` оставлен пустым, поэтому браузер отправляет запросы по относительным путям к своему же источнику `http://127.0.0.1:5173`, а правила в [`vite.config.ts`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/vite.config.ts>) пересылают их на адрес из `WEBUI_BACKEND_URL`. Старые примеры, где браузер всегда обращается напрямую к порту `8080` в обход Vite, к этому выпуску переносить не нужно.
- **Серверная часть (backend)** работает на Python и FastAPI по адресу `http://127.0.0.1:8080`. Она отвечает за конфигурацию, создание учётной записи, проверку входа и хранение базы данных. Без работающего backend и успешного входа в аккаунт интерфейс не откроет рабочую область с боковой панелью, где находится нужная кнопка нового чата.

Цепочка для нашего эксперимента с подписью выглядит так: браузер открывает `http://127.0.0.1:5173` -> Vite отдаёт интерфейс и принимает относительные запросы API и WebSocket -> прокси Vite пересылает их на backend `http://127.0.0.1:8080`. Обращения к серверу моделей вроде Ollama на `http://127.0.0.1:11434` идут отдельно, от Python-процесса backend; получение списка моделей и генерация ответа относятся к этой отдельной цепочке. Сама видимая метка `· dev` формируется в шаблоне интерфейса, поэтому скачивать и запускать чатовую модель ради проверки этой правки не нужно.

Прежде чем клонировать репозиторий и запускать команды, подготовим изолированную папку и проверим инструменты, чтобы эксперимент не затронул вашу основную установку.

## Подготовьте отдельную лабораторию и проверьте инструменты

На каком компьютере повторять эти шаги и как устроить каталоги, чтобы не повредить уже существующие базы и ключи? Мы выбираем один фиксированный маршрут: нативная Ubuntu 24.04 x86\_64, обычная учётная запись пользователя без `sudo` для пакетных менеджеров и оболочка Bash.

Для этого маршрута в системе заранее должны быть установлены Git, Node.js серии `22.x` (не ниже `22.10`), npm `10`, Python `3.11` вместе с модулями `venv` и `pip`, а также утилиты `curl` и `ss`. Такой выбор опирается на пересечение требований в [руководстве по разработке](<https://raw.githubusercontent.com/open-webui/docs/0b8f7318d88208257da1135675b6ab6cc9d3046d/docs/getting-started/advanced-topics/development.md>) , ограничений `requires-python` в [`pyproject.toml`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/pyproject.toml>) , поля `engines` в [`package.json`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/package.json>) и зависимостей в [`package-lock.json`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/package-lock.json>) . Хотя первичная документация допускает и Python `3.12`, ниже мы используем только команды для `python3.11`; не подставляйте сюда несовместимые новые выпуски вроде Python `3.13` или Node.js `24`.

Все файлы мы разместим внутри нового лабораторного каталога `$HOME/open-webui-source-lab`, разделив его на независимые части:

- `$HOME/open-webui-source-lab/source` — дерево исходного кода (checkout репозитория);
- `$HOME/open-webui-source-lab/venv` — виртуальное окружение с установленными Python-пакетами;
- `$HOME/open-webui-source-lab/data` — каталог данных `DATA_DIR` для базы SQLite и локального состояния именно этого экземпляра;
- `$HOME/open-webui-source-lab/webui-secret-key` — постоянный файл ключа подписи сессий, лежащий рядом с папками, но вне дерева исходников.

Такое разделение защищает от частой путаницы. Согласно [документации стандартного модуля `venv`](<https://docs.python.org/3.11/library/venv.html>) , виртуальное окружение изолирует только интерпретатор и установленные библиотеки Python, но не создаёт контейнер, не ограничивает сетевой доступ процесса и не отделяет данные приложения. В то же время код [`backend/open_webui/env.py`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/backend/open_webui/env.py>) и скрипт [`backend/start.sh`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/backend/start.sh>) по умолчанию ищут данные и файл ключа относительно рабочего каталога `backend`, а при первом запуске могут применить миграции базы данных. Если подключить разработку к рабочей папке данных или скопировать чужой `.env`, можно испортить существующую базу. Поэтому мы создаём новую лабораторию, не копируем в неё рабочие базы, ключи или шаблон [`.env.example`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/.env.example>) (где разрешены широкие настройки `CORS_ALLOW_ORIGIN=*` и `FORWARDED_ALLOW_IPS=*`) и никогда не выводим содержимое секретного ключа в терминал.

В качестве кода мы закрепляем выпуск [`v0.12.3`](<https://api.github.com/repos/open-webui/open-webui/releases/latest>) (опубликован 16 октября 2026 года, проверен по источникам 19 октября 2026 года), которому соответствует точный коммит [`91ea4e3421d1d8069f13d4a463fcb5986d94ea60`](<https://api.github.com/repos/open-webui/open-webui/git/ref/tags/v0.12.3>) . Это фиксированный учебный снимок релизного кода, выбранный для того, чтобы пути к файлам и номера строк совпадали до символа. Текущая совместная разработка в официальном репозитории ведётся в ветке `dev`.

Откройте терминал подготовки и проверьте версии инструментов, а также отсутствие активных слушателей на портах `5173` и `8080`:

```
git  --version  
node  --version  
npm  --version  
python3.11  --version  
ss  -ltn  '( sport = :5173 or sport = :8080 )'  
```

Сравните вывод первых четырёх команд с выбранными версиями: Node.js должен принадлежать ветке `v22.x` (не ниже `22.10.0`), `npm` — версии `10.x`, а `python3.11` должен откликаться без ошибки. Вывод `ss` показывает сокеты в состоянии прослушивания (`LISTEN`) и должен остаться без строк на портах `:5173` и `:8080`. Если нужная версия инструмента отсутствует или хотя бы один из портов уже занят, остановитесь на этом шаге: выясните, какой процесс занимает порт, и не завершайте наугад чужие службы.

Когда условия совпали и оба порта свободны, можно один раз создать каталог лаборатории, получить исходники и установить зависимости.

## Получите закреплённые исходники и установите зависимости

Как скачать именно проверенную ревизию кода и почему установка зависимостей по зафиксированным спискам всё равно может обращаться к внешним серверам и выполнять код? Разобьём подготовку на два последовательных блока в терминале. В каждом блоке команда `set -e` немедленно останавливает выполнение, если любой шаг завершится с ошибкой.

Выполните первый блок однократно в отдельной сессии Bash, чтобы создать структуру каталогов и клонировать выпуск `v0.12.3`:

```
set  -e  
DEV_LAB = " $HOME /open-webui-source-lab"  
umask  077  
test  !  -e  " $DEV_LAB "  
mkdir  -m  700  " $DEV_LAB "  
mkdir  -m  700  " $DEV_LAB /data"  
git  clone  --branch  v0.12.3  --depth  1  https://github.com/open-webui/open-webui.git  " $DEV_LAB /source"  
cd  " $DEV_LAB /source"  
test  "$( git  rev-parse HEAD)"  =  '91ea4e3421d1d8069f13d4a463fcb5986d94ea60'  
test  !  -e  .env  
```

Разберём защитные проверки этого шага:

- `umask 077` и `mkdir -m 700` ограничивают доступ к создаваемым папкам лаборатории и будущим файлам только текущим пользователем.
- `test ! -e "$DEV_LAB"` проверяет, что пути `$HOME/open-webui-source-lab` ещё не существует. Если каталог уже есть, скрипт сразу остановится: не очищайте старую папку автоматически и не клонируйте код поверх прежних данных.
- Клонирование по тегу `--branch v0.12.3 --depth 1` переводит репозиторий в состояние `detached HEAD` — это нормально и ожидаемо для закреплённого снимка выпуска. Не выполняйте здесь `git pull` и не переключайтесь на плавающую ветку.
- Проверка `git rev-parse HEAD` гарантирует, что вы получили ровно коммит `91ea4e3421d1d8069f13d4a463fcb5986d94ea60`, а `test ! -e .env` подтверждает, что в корне репозитория нет файла `.env`. Сюда не нужно копировать `.env`, `.env.local` или `.env.development` из других проектов.

Теперь в сессии Bash выполните второй блок — установку JavaScript- и Python-зависимостей из корня полученного дерева `$DEV_LAB/source`:

```
set  -e  
DEV_LAB = " $HOME /open-webui-source-lab"  
cd  " $DEV_LAB /source"  
test  !  -e  .env  
npm  ci  
python3.11  -m  venv  " $DEV_LAB /venv"  
" $DEV_LAB /venv/bin/python"  -m  pip  install  -r  backend/requirements.txt  
```

Как объясняет [документация `npm ci`](<https://docs.npmjs.com/cli/v10/commands/npm-ci/>) , эта команда устанавливает пакеты строго по существующему [`package-lock.json`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/package-lock.json>) , не перезаписывает lock-файл и завершается ошибкой при расхождении с `package.json`. Однако `npm ci` — это не просто пассивная распаковка архивов: установка выполняется с правами вашего пользователя и может запускать lifecycle-скрипты пакетов (в lock-файле этого выпуска флаг `hasInstallScript` отмечен, например, у `cypress`, `esbuild`, `onnxruntime-node`, `sharp`, `core-js`, `protobufjs`) и догружать платформенные бинарные файлы. Со стороны Python файл [`backend/requirements.txt`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/backend/requirements.txt>) фиксирует прямые зависимости, но не закрепляет хеши всех транзитивных пакетов и бинарных `wheel`-архивов под конкретную систему.

Поэтому фиксация коммита не делает установку полностью автономной или побайтово воспроизводимой. Если `npm ci` или `pip install` завершились ошибкой, остановитесь и разберите сообщение: не запускайте `sudo npm`, не добавляйте `--force` и не редактируйте `package-lock.json` или `requirements.txt` ради обхода сбоя.

Наш короткий маршрут отличается от [официального руководства по разработке](<https://raw.githubusercontent.com/open-webui/docs/0b8f7318d88208257da1135675b6ab6cc9d3046d/docs/getting-started/advanced-topics/development.md>) : там перед первым запуском `npm run dev` предлагается выполнить полную сборку `npm run build`. В закреплённом коде [`backend/open_webui/main.py`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/backend/open_webui/main.py>) предусмотрена работа серверной части в режиме API без каталога собранного статического интерфейса `build`, а сам редактируемый интерфейс в режиме разработки отдаёт Vite. Поэтому здесь шаг `npm run build` мы пропускаем как ограниченную адаптацию для минимального UI-эксперимента.

Код и библиотеки подготовлены. Теперь первым запустим серверный процесс backend, чтобы он инициализировал пустую базу данных, создал постоянный ключ и подготовил API для будущего интерфейса.

## Запустите backend в терминале A

Как запустить серверную часть так, чтобы она слушала только локальный интерфейс `127.0.0.1`, сохраняла данные и ключ на своих местах и не унаследовала случайные переменные из вашего рабочего окружения?

В официальном руководстве для этого упоминается короткий скрипт [`backend/dev.sh`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/backend/dev.sh>) . Но если заглянуть в его исходный код для выпуска `v0.12.3`, обнаружатся две проблемы: во-первых, он запускает `uvicorn` с жёстко заданным адресом `--host 0.0.0.0` (открывая порт всем сетевым интерфейсам машины), а во-вторых, не создаёт файл секретного ключа. Между тем в [`backend/open_webui/env.py`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/backend/open_webui/env.py>) аутентификация `WEBUI_AUTH` по умолчанию включена (`True`), а пустой ключ `WEBUI_SECRET_KEY` вызывает немедленную ошибку при старте. Отключать аутентификацию ради обхода этой проверки нельзя.

Вместо этого мы используем штатный скрипт [`backend/start.sh`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/backend/start.sh>) и передадим ему аргумент `--reload`. Этот скрипт учитывает переменные `HOST`, `PORT` и `WEBUI_SECRET_KEY_FILE`: перед запуском сервера он проверяет наличие файла ключа, создаёт его при первом старте и экспортирует значение в процесс, не печатая сам секрет в консоль. Когда в `start.sh` передаётся аргумент `--reload`, он подставляется в вызов `uvicorn` вместо стандартного набора аргументов с количеством воркеров (`--workers`), поэтому сервер запускается в режиме разработки с отслеживанием изменений Python-кода.

Откройте отдельный терминал **A**, выполните следующий блок и оставьте процесс работать:

```
set  -e  
DEV_LAB = " $HOME /open-webui-source-lab"  
cd  " $DEV_LAB /source/backend"  
test  !  -e  ../.env  
test  -d  " $DEV_LAB /data"  
umask  077  
env  -i  HOME=" $HOME "  PATH=" $DEV_LAB /venv/bin: $PATH "  LANG="${ LANG :- C . UTF-8 }"  \  
  ENV=dev  HOST= 127.0.0.1  PORT= 8080  WEBUI_AUTH=True  \  
  DATA_DIR=" $DEV_LAB /data"  \  
  WEBUI_SECRET_KEY_FILE=" $DEV_LAB /webui-secret-key"  \  
  CORS_ALLOW_ORIGIN='http://127.0.0.1:5173;http://127.0.0.1:8080'  \  
  FORWARDED_ALLOW_IPS= 127.0.0.1  \  
  bash  start.sh  --reload  
```

Параметры команды:

- **`env -i`** запускает процесс в очищенном окружении, сохраняя только `HOME`, `PATH` (где папка `$DEV_LAB/venv/bin` поставлена первой) и локаль `LANG`. Это гарантирует, что в лабораторный процесс не попадут унаследованные из вашей оболочки переменные `DATABASE_URL`, `WEBUI_SECRET_KEY`, настройки Redis или ключи внешних API. Как видно в [`backend/open_webui/env.py`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/backend/open_webui/env.py>) , переменная `DATA_DIR` управляет путём к SQLite только тогда, когда не задан `DATABASE_URL`; а загрузка `.env` через [`python-dotenv`](<https://raw.githubusercontent.com/theskumar/python-dotenv/main/src/dotenv/main.py>) при переданном абсолютном пути `BASE_DIR/.env` проверяет именно корневой `.env` репозитория, отсутствие которого мы перед стартом подтверждаем командой `test ! -e ../.env`. `env -i` очищает переменные среды, но не изолирует файловую систему пользователя.
- **`HOST=127.0.0.1` и `PORT=8080`** привязывают слушатель Uvicorn к локальному интерфейсу обратной петли (loopback).
- **`DATA_DIR="$DEV_LAB/data"` и `WEBUI_SECRET_KEY_FILE="$DEV_LAB/webui-secret-key"`** закрепляют внешние пути для базы данных и постоянного ключа. При перезагрузке кода и повторных запусках эти пути должны оставаться неизменными, чтобы сессии и данные не терялись.
- **`WEBUI_AUTH=True`** явно сохраняет штатный режим со входом по учётной записи.
- **`CORS_ALLOW_ORIGIN='http://127.0.0.1:5173;http://127.0.0.1:8080'`** перечисляет источники (`Origin`), которым браузерная политика CORS разрешает обращаться к API. CORS — это правило для браузера, а не межсетевой экран (firewall), и само по себе оно не заменяет привязку `HOST=127.0.0.1`.

Не удивляйтесь, если первый запуск в терминале A займёт заметное время и обратится в сеть. По коду [`backend/open_webui/main.py`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/backend/open_webui/main.py>) , [`routers/retrieval.py`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/backend/open_webui/routers/retrieval.py>) и [`retrieval/utils.py`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/backend/open_webui/retrieval/utils.py>) при стандартном старте инициализируется локальная модель эмбеддингов через `SentenceTransformer` и `snapshot_download` с Hugging Face. Это вспомогательный механизм поиска по документам, а не языковая модель для чата. Мы намеренно не добавляем в команду искусственные флаги отключения RAG или OpenAI, чтобы не усложнять конфигурацию, но и не считаем появление первых строк лога достаточным доказательством готовности сервера.

Оставьте терминал A открытым: пока он держит запущенный API, мы поднимем во втором окне сервер интерфейса.

## Запустите интерфейс в терминале B

Как запустить редактируемый интерфейс так, чтобы сохранились все подготовительные проверки проекта, а сервер разработки Vite слушал только локальный адрес `127.0.0.1:5173` и знал, куда пересылать запросы API?

В [`package.json`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/package.json>) скрипт `dev` устроен в три шага: сначала автоматически срабатывает `predev` (`npm run crypto:verify`), затем `npm run pyodide:fetch` и только после этого запускается `vite dev --host`. Вызывать бинарный файл `vite` напрямую в обход `npm run dev` не стоит по двум причинам:

1. Вы пропустите штатную подготовку: скрипт [`scripts/encryption/artifact.mjs`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/scripts/encryption/artifact.mjs>) сверяет контрольные суммы зафиксированных в репозитории криптографических артефактов (для неизменённого кода это не требует компилятора Rust), а [`scripts/prepare-pyodide.js`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/scripts/prepare-pyodide.js>) подготавливает в `static/pyodide` ресурсы браузерного окружения Pyodide, при необходимости скачивая пакеты с CDN и метаданные `wheel`-пакетов из PyPI.
2. Как видно в [`vite.config.ts`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/vite.config.ts>) , константа `APP_VERSION` берётся из переменной `process.env.npm_package_version`, которую автоматически подставляет именно запуск через `npm run`.

При этом в стандартном скрипте `dev` уже записан флаг `--host` без адреса, который в [конфигурации сервера Vite 5](<https://v5.vite.dev/config/server-options.html>) открывает доступ со всех сетевых адресов. Чтобы сузить прослушивание до локальной петли, мы передадим после разделителя `--` явные параметры `--host 127.0.0.1 --port 5173 --strictPort`. В используемой версии CLI [`vite` 5.4.21](<https://raw.githubusercontent.com/vitejs/vite/v5.4.21/packages/vite/src/node/cli.ts>) (поверх парсера аргументов [`cac` 6.7.14](<https://registry.npmjs.org/cac/-/cac-6.7.14.tgz>) ) при повторении опции применяется последнее переданное значение, поэтому завершающий `--host 127.0.0.1` ограничивает адрес Vite локальным интерфейсом, а `--strictPort` запрещает незаметно переключаться на порт `5174`, если `5173` занят.

Откройте второй терминал **B** (не закрывая терминал A), выполните команду запуска интерфейса и оставьте её работать:

```
set  -e  
DEV_LAB = " $HOME /open-webui-source-lab"  
cd  " $DEV_LAB /source"  
test  !  -e  .env  
env  -i  HOME=" $HOME "  PATH=" $PATH "  LANG="${ LANG :- C . UTF-8 }"  \  
  WEBUI_BACKEND_URL=http://127.0.0.1:8080  \  
  npm  run  dev  --  --host  127.0.0.1  --port  5173  --strictPort  
```

Переменная `WEBUI_BACKEND_URL=http://127.0.0.1:8080` явно задаёт IPv4-адрес для встроенного в [`vite.config.ts`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/vite.config.ts>) прокси. Когда вы откроете в браузере этой же машины адрес `http://127.0.0.1:5173`, все обращения интерфейса к путям `/api`, `/ollama`, `/openai`, `/oauth` и `/ws` (включая WebSocket-подключения для `/api` и `/ws`) пойдут на `127.0.0.1:5173`, а Vite перенаправит их процессу из терминала A на `http://127.0.0.1:8080`.

Если на этапе `crypto:verify`, загрузки Pyodide или привязки порта `5173` команда в терминале B остановилась с ошибкой, не ослабляйте проверку целостности и не переходите к следующему порту: сначала устраните причину остановки.

Когда оба процесса запущены в терминалах A и B, нужно последовательно проверить их фактические сетевые адреса, готовность API и вход в учётную запись — прежде чем менять код интерфейса.

## Проверьте адреса, API и вход

Что именно должно ответить в системе до того, как мы откроем редактор кода и начнём править компонент боковой панели? Сам по себе текст в терминалах A и B ещё не доказывает, что сокеты привязаны к `127.0.0.1`, база данных инициализирована, прокси Vite пропускает запросы, а интерфейс пускает вас внутрь.

Откройте третий терминал **C** (процессы в A и B продолжают работать) и выполните три проверочные команды:

```
ss  -ltnp  '( sport = :5173 or sport = :8080 )'  
curl  -fsS  http://127.0.0.1:8080/ready  
curl  -f  -s  -o  /dev/null  -w  '%{http_code}\n'  http://127.0.0.1:5173/api/config  
```

Каждая строка здесь проверяет свой уровень связки:

| Проверка | Что ожидаем увидеть | Что именно это подтверждает |
| --- | --- | --- |
| `ss -ltnp '( sport = :5173 or sport = :8080 )'` | Слушатели привязаны к `127.0.0.1:5173` и `127.0.0.1:8080` | Оба сервера действительно слушают локальный loopback-адрес, а не `0.0.0.0` или случайный соседний порт |
| `curl -fsS http://127.0.0.1:8080/ready` | Успешный ответ `{"status":true}` без ошибки `curl` | Серверная часть в [`backend/open_webui/main.py`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/backend/open_webui/main.py>) завершила инициализацию старта и проверила доступ к базе данных (а также Redis, если бы он использовался), в отличие от поверхностного маршрута `/health` |
| `curl ... http://127.0.0.1:5173/api/config` | Код ответа `200` | Запрос к порту интерфейса `5173` успешно проходит через прокси Vite на backend `8080` и возвращает конфигурацию |

Если `ss` показывает привязку к `0.0.0.0` или отсутствие одного из портов, либо любой из вызовов `curl` завершается ошибкой, остановитесь на этом шаге и проверьте вывод в терминалах A и B.

Код `200` от `/api/config` доказывает, что Vite и FastAPI связаны через прокси, но сам по себе ещё не открывает боковую панель в браузере. Чтобы увидеть кнопку нового чата, нужно пройти аутентификацию.

Откройте в браузере `http://127.0.0.1:5173` и создайте **отдельную лабораторную учётную запись** с новым паролем, который не совпадает с вашими рабочими паролями. Согласно логике [`backend/open_webui/routers/auths.py`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/backend/open_webui/routers/auths.py>) , первый единственный зарегистрированный пользователь в новой базе автоматически получает роль администратора (`admin`), после чего свободная регистрация новых пользователей в конфигурации отключается (подробнее устройство первой учётной записи разобрано в уроке про [первый вход и настройки доступа](<https://timofei-ermolaev.ru/courses/open-webui/configure/first-admin/>) ). После входа убедитесь, что левая боковая панель (sidebar) открыта и на ней видна кнопка создания нового чата.

Только теперь, когда перед глазами в браузере открыта боковая панель вашего лабораторного экземпляра, у нас есть точное место для наблюдения за правкой кода.

## Добавьте метку к подписи и верните исходную строку

Как убедиться, что браузер отображает именно ваш изменённый исходный код, и не задеть при этом логику работы кнопки, переводы или лицензионные элементы?

В дереве исходников за левую боковую панель отвечает файл [`src/lib/components/layout/Sidebar.svelte`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/src/lib/components/layout/Sidebar.svelte>) (в нашей лаборатории его полный путь — `$HOME/open-webui-source-lab/source/src/lib/components/layout/Sidebar.svelte`). В зафиксированном коммите `91ea4e3421d1d8069f13d4a463fcb5986d94ea60` на строке `1234` находится блок видимой подписи кнопки нового чата. В оригинале эта строка выглядит так:

```
< div  class = " self-center text-[0.8125rem] leading-5" >{$i18n. t ( 'New Chat' )}</ div >  
```

Выражение `{$i18n.t('New Chat')}` обращается к системе локализации из [`src/lib/i18n/index.ts`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/src/lib/i18n/index.ts>) и подставляет надпись на текущем языке интерфейса (например, «Новый чат» или «New Chat»). Мы не будем редактировать JSON-словари переводов, чтобы результат не зависел от выбранного языка и кеша словарей. Не будем трогать и соседний обработчик нажатия `newChatHandler`, атрибут доступности `aria-label`, а также название продукта `APP_NAME`, логотипы или условия брендинга и лицензии, упомянутые в [`src/lib/constants.ts`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/src/lib/constants.ts>) .

Откройте файл `$HOME/open-webui-source-lab/source/src/lib/components/layout/Sidebar.svelte` в текстовом редакторе, найдите эту единственную строку `1234` и добавьте после закрывающей фигурной скобки суффикс ` · dev`, чтобы строка приняла точный вид:

```
< div  class = " self-center text-[0.8125rem] leading-5" >{$i18n. t ( 'New Chat' )} · dev</ div >  
```

Сохраните файл и посмотрите на открытую боковую панель в браузере на `http://127.0.0.1:5173`. Сервер Vite отслеживает изменения файлов `.svelte` и передаёт обновление через механизм горячей замены модулей (HMR); если браузер не обновил подпись сразу, обновите страницу вручную. Рядом с текстом кнопки нового чата должна появиться метка `· dev`. Если метка не появилась, проверьте, что боковая панель развёрнута, а вы отредактировали и сохранили именно файл внутри `$HOME/open-webui-source-lab/source`: подключать языковую модель для поиска причины здесь не нужно, так как модель в отрисовке этого шаблона не участвует.

Убедившись, что метка `· dev` появилась, сразу выполните обратный шаг: верните в `$HOME/open-webui-source-lab/source/src/lib/components/layout/Sidebar.svelte` исходную строку без суффикса, снова сохраните файл и проверьте в браузере, что метка `· dev` исчезла. Для возврата редактируйте только эту строку вручную — не используйте разрушительные команды вроде `git reset --hard` или `git clean`.

После того как подпись в браузере вернулась к исходному виду, проверьте в терминале **C**, что в файле `Sidebar.svelte` не осталось незамеченных правок:

```
DEV_LAB = " $HOME /open-webui-source-lab"  
cd  " $DEV_LAB /source"  
git  diff  --  src/lib/components/layout/Sidebar.svelte  
```

Пустой вывод `git diff` для этого пути подтверждает, что файл точно возвращён к состоянию выпуска `v0.12.3`. Два противоположных наблюдения в браузере (появление `· dev` после сохранения и исчезновение после возврата) связывают конкретную строчку исходного кода с живым интерфейсом. Такая проверка подтверждает только цепочку отображения UI-компонента: она не проверяет сборку production-бандла, работу всех разделов приложения или генерацию ответов моделью.

## Остановите процессы и сохраните лабораторию

Как аккуратно завершить текущий сеанс разработки, чтобы в следующий раз не проходить клонирование и установку пакетов заново, и какие задачи остаются за рамками нашего UI-эксперимента?

Разделим четыре действия, которые легко смешать при первом знакомстве с разработкой:

1. **Обновление интерфейса (Vite HMR)** срабатывает в терминале B, когда вы сохраняете файл фронтенда вроде `Sidebar.svelte`.
2. **Перезагрузка серверного процесса (`--reload`)** срабатывает в терминале A под управлением Uvicorn, когда меняются файлы Python в `backend`.
3. **Остановка процессов (`Ctrl+C`)** завершает работу серверов в терминалах A и B, освобождая порты `8080` и `5173`.
4. **Повторный запуск сеанса** снова поднимает те же два процесса поверх уже существующих папок `source`, `venv`, `data` и файла `webui-secret-key`.

Ни один из этих механизмов не является промышленным развёртыванием (production deployment), отказоустойчивым кластером или резервной копией. Аналогично, удаление или пересоздание папки `venv` меняет только набор Python-библиотек и не заменяет резервное копирование базы в `data`.

Чтобы закончить работу сейчас, перейдите по очереди в терминалы **B** и **A** и нажмите `Ctrl+C` в каждом из них. Оба процесса остановятся, а папка `$HOME/open-webui-source-lab/data` с вашей лабораторной учётной записью и файл `$HOME/open-webui-source-lab/webui-secret-key` останутся на диске. Когда вы захотите продолжить эксперименты с этой же копией позже, **не повторяйте** одноразовый блок создания каталога с `test ! -e "$DEV_LAB"` и `git clone`, а также установку зависимостей: достаточно снова запустить блок backend в терминале A и блок frontend в терминале B с теми же путями.

Что остаётся отдельными задачами, если вы решите пойти дальше?

- **Проверка ответа языковой модели.** Только если на этой же машине у вас уже запущена локальная служба Ollama и в ней уже скачана нужная модель, вы можете после UI-эксперимента по желанию проверить генерацию ответа по опубликованному уроку про [подключение локальной модели через Ollama](<https://timofei-ermolaev.ru/courses/open-webui/configure/ollama/>) . Поскольку в нашем маршруте Python-процесс backend работает нативно в самой ОС Ubuntu (а не внутри контейнера Docker), локальный сервер Ollama на той же машине может быть доступен для него по обычному адресу `http://127.0.0.1:11434`; правила контейнерного доступа с `host.docker.internal` сюда переносить не нужно. Учтите также особенность [`backend/open_webui/config.py`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/backend/open_webui/config.py>) : после первой записи настроек в базу данных сохранённые в БД адреса подключений имеют приоритет над новыми переменными окружения. Если вы получите ответ модели, это подтвердит отдельную цепочку `backend -> Ollama -> модель`.
- **Участие в разработке официального проекта (upstream contribution).** Наша лаборатория развёрнута на фиксированном релизном теге `v0.12.3`, тогда как в [официальном руководстве по разработке](<https://raw.githubusercontent.com/open-webui/docs/0b8f7318d88208257da1135675b6ab6cc9d3046d/docs/getting-started/advanced-topics/development.md>) работа над новыми изменениями ведётся в ветке `dev`. Кроме того, согласно [актуальным правилам участия в проекте (`contributing.mdx`)](<https://raw.githubusercontent.com/open-webui/docs/0b8f7318d88208257da1135675b6ab6cc9d3046d/docs/contributing.mdx>) , запросы на включение кода (code pull requests), за исключением правок перевода и локализации (`i18n`), принимаются только по явному предварительному запросу мейнтейнеров. Поэтому локальный эксперимент в своей лаборатории не означает автоматической отправки PR в основной репозиторий.

Появление и исчезновение метки `· dev` в `Sidebar.svelte` при пустом итоговом `git diff` наглядно связывают конкретную строку исходного кода с тем, что рисует в браузере сервер Vite. При этом готовность API (`/ready`), работа прокси (`/api/config`), вход в учётную запись и связь с языковой моделью проверяются как отдельные звенья, а постоянные данные и ключ подписи живут вне дерева исходников и виртуального окружения Python. Завершите процессы в терминалах A и B сочетанием `Ctrl+C`, сохранив папку `$HOME/open-webui-source-lab`: для следующего локального изменения вам понадобится повторить только два блока запуска серверов.

## Материалы урока

- [Установка Open WebUI через Docker Compose timofei-ermolaev.ru](<https://timofei-ermolaev.ru/courses/open-webui/install/docker-compose/>)
- [Open WebUI через Python: uv и изолированное окружение timofei-ermolaev.ru](<https://timofei-ermolaev.ru/courses/open-webui/drugie-sposoby/open-webui-python/>)
- [Первый вход и базовые настройки доступа timofei-ermolaev.ru](<https://timofei-ermolaev.ru/courses/open-webui/configure/first-admin/>)
- [Подключаем локальную модель через Ollama timofei-ermolaev.ru](<https://timofei-ermolaev.ru/courses/open-webui/configure/ollama/>)

## Отметить прохождение

Отметьте урок после того, как получили проверяемый результат из практической части.

Урок пройден

![Тимофей Ермолаев](https://timofei-ermolaev.ru/_astro/timofei-ermolaev-portrait.s0mCFw-J_2r7Nkv.webp)

## Об авторе

### Тимофей Ермолаев

Директор по информационным технологиям (CIO)

Пишу о больших языковых моделях, автоматизации и применении ИИ в бизнесе. В статьях разделяю личный опыт, выводы исследований и практические рекомендации.

[Подробнее об авторе](<https://timofei-ermolaev.ru/ob-avtore/>)
