Open WebUI в Dockhost

Поможем разместить Open WebUI в Dockhost: настроим постоянный диск, администратора до открытия доступа, HTTPS и проверку данных после перезапуска.

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

  • Различать контейнер, постоянный диск, домен и маршрут в Dockhost
  • Подготовить одного администратора и стабильный ключ до публикации приложения
  • Связать HTTPS адрес с внутренним портом и отдельно проверить готовность и модель
  • Проверить прежний аккаунт и состояние после штатного перезапуска

Проверяемый результат: Новый экземпляр Open WebUI настроен по документированной схеме с одной репликой и SSD RWO; читатель умеет проверить первый вход, HTTPS и сохранение состояния при штатном перезапуске. Ответ модели проверяется отдельно при подключённом API.

Если чат-интерфейс нужен и за пределами домашнего ноутбука, его можно перенести на постоянную площадку. Dockhost держит контейнер запущенным, подключает постоянный диск и открывает веб-интерфейс по HTTPS. Обслуживать виртуальную машину и настраивать сертификаты в Linux вручную не придётся. За сохранность ключа, выбор хранилища, закрытие регистрации и оплату ресурсов отвечает владелец проекта.

Мы настроим новый экземпляр Open WebUI: сначала сохраним данные и создадим владельца, затем проверим запуск, откроем HTTPS и подключим модель по API. После перезапуска проверим прежнюю учётную запись и настройки.

Если вы уже работали с Docker Compose, связка образа, постоянного хранилища и секретного ключа знакома. Здесь мы зададим её в панели платформы. Инструкция составлена по официальной документации Dockhost и исходному коду Open WebUI v0.12.1. Манифест образа в GHCR проверен; сам контейнер в Dockhost автор не запускал. Ниже описаны ожидаемые результаты и условия, при которых нужно остановиться.

Подготовим проект и адрес будущего интерфейса

Платформа размещает веб-интерфейс и хранит его базу данных, а ответы генерирует внешний поставщик модели по API. Существующие локальные чаты, учётные записи и ключи в новый облачный экземпляр автоматически не переносятся. Здесь мы настраиваем отдельный контейнер с нуля, без локальной Ollama, GPU и Helm-чартов.

Начнём с прав доступа, лимитов и тарифов в личном кабинете. Вычислительные ресурсы, постоянный диск и запросы к модельному API оплачиваются отдельно. Если в проекте есть незнакомые сервисы или стоимость ресурсов неясна, новую конфигурацию пока не применяем. Для установки подойдёт чистый проект либо существующий, в котором мы заранее проверили, что имена диска и контейнера свободны.

Домен подготовим заранее: его адрес понадобится приложению при первом запуске. Свяжем домен с контейнером только после внутренней инициализации. В разделе «Сетевые сервисы» → «Домены» добавим динамический домен третьего уровня платформы или укажем собственный домен по инструкции создания домена. Для собственного домена предварительно потребуется настроить A-запись у регистратора на выданный платформой IP-адрес; в обоих случаях Dockhost автоматически выпускает SSL-сертификат Let’s Encrypt.

Сохраним доменное имя. Ниже HTTPS_ORIGIN означает полный адрес вида https://ваш-домен со схемой https:// и без завершающего слэша. В разделе «Маршруты» пока ничего не создаём. Будущий адрес уже известен, старые сервисы не затронуты, а этот домен ещё не направляет запросы к нашему приложению. Теперь подготовим постоянный диск и ключ подписи, которые должны пережить замену контейнера.

Создадим диск для данных Open WebUI

Файловая система контейнера временная. В ней могут оставаться рабочие файлы текущего запуска, но постоянные данные нужно вынести на отдельный диск. В контейнере он будет виден как папка: Open WebUI сохранит в ней базу SQLite, учётные записи, настройки и историю диалогов. Профиль администратора хранится в этой базе, а не привязывается к доменному имени.

Для надёжной работы встроенной базы данных важны одновременно путь подключения, режим доступа к диску и свойства самого хранилища:

  1. В разделе «Сетевые диски» создайте диск типа SSD со свободным понятным именем, например open-webui-data, руководствуясь документацией по сетевым дискам. Объём выберите под свои задачи и планируемые загрузки файлов в чат; универсального минимального порога для всех сценариев нет.
  2. Выберите режим работы ReadWriteOnce (RWO). По описанию режимов сетевых дисков Dockhost том RWO подключается только к одной реплике контейнера, а при перезапуске старая реплика сначала полностью останавливается перед созданием новой. Режим ReadWriteMany (RWM) для совместной записи из нескольких контейнеров здесь не подходит.
  3. Проверим требования СУБД. В документации Dockhost диски HDD и SSD описаны как сетевое блочное хранилище, а режим RWO прямо рекомендован платформой для встраиваемых баз данных вроде SQLite и DuckDB. Руководство Open WebUI по развёртыванию разрешает для SQLite только один экземпляр приложения на подходящем локальном или блочном хранилище с надёжными файловыми блокировками и низкой задержкой и запрещает сетевые файловые системы вроде NFS и SMB. В документации SQLite по режиму WAL также указано, что механизмы общей памяти требуют работы всех обращающихся к базе процессов на одном хосте.

Сетевое блочное хранилище и сетевая файловая система NFS устроены по-разному. Название режима RWO и отсутствие ошибок в первые минуты ещё не подтверждают совместимость с файловыми блокировками и fsync. Перед переносом важных рабочих процессов уточним у оператора, подходит ли выбранный диск для SQLite в режиме WAL. Если хранилище основано на NFS, SMB или CephFS, оператор не подтверждает корректную работу блокировок либо в логах появляются ошибки database is locked и сбои WAL, эту схему использовать нельзя. Отключение блокировок и увеличение числа реплик проблему не решают.

Созданный SSD-диск в режиме RWO мы подключим в каталог /app/backend/data на следующем шаге. Он сохраняет данные при перезапуске контейнера, но не заменяет отдельную резервную копию. Осталось связать его с образом и заранее задать владельца.

Зададим контейнер и первого администратора

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

Откроем «Контейнеры» → «Добавить» и заполним параметры по инструкции создания контейнера, настройки портов и подключения сетевых дисков:

  • Имя контейнера: open-webui. Платформа создаст одноимённый внутренний сетевой сервис open-webui, как указано в обзоре контейнеров Dockhost.
  • Образ: ghcr.io/open-webui/open-webui:v0.12.1. Мы фиксируем тег выпуска v0.12.1, чтобы настройки и логи соответствовали исходному коду урока.
  • Количество реплик: 1 для работы с диском RWO и SQLite.
  • Порты: 8080/TCP. В образе задан PORT=8080; директива EXPOSE 8080 декларирует порт в Dockerfile v0.12.1. Конструкция 3000:8080 из локального Docker Compose здесь не нужна: внешний трафик пойдёт через маршрутизатор платформы.
  • Сетевые диски: созданный SSD-диск open-webui-data с точкой монтирования /app/backend/data.
  • Команда запуска и пользователь: штатные значения образа без переопределения. По умолчанию контейнер выполняет bash start.sh.

До первого нажатия «Применить» добавим в настройки контейнера шесть постоянных переменных окружения, сверяясь со справочником конфигурации Open WebUI, рекомендациями по HTTPS и определением переменных в backend/open_webui/env.py:

ПеременнаяЗначениеНазначение
UVICORN_WORKERS1Ограничивает сервер одним рабочим процессом в выбранной схеме с одной репликой и SQLite
WEBUI_AUTHTrueОставляет обязательную авторизацию включённой
ENABLE_SIGNUPFalseЗапрещает самостоятельную регистрацию новых пользователей
WEBUI_SECRET_KEYСвоё постоянное случайное значение (вводится приватно)Подписывает сессионные токены и защищает чувствительные поля
WEBUI_URLФактический HTTPS_ORIGIN подготовленного доменаЗадаёт базовый публичный адрес интерфейса
CORS_ALLOW_ORIGINТот же точный HTTPS_ORIGINЗадаёт разрешённый origin для CORS

Для WEBUI_SECRET_KEY сгенерируем на своём компьютере длинную случайную строку, например через openssl rand -hex 32, и сохраним её в личном менеджере паролей. Этот же ключ понадобится при любых пересозданиях контейнера и восстановлении из резервной копии. В WEBUI_URL и CORS_ALLOW_ORIGIN подставьте реальный адрес вида https://ваш-домен, полученный в первом разделе.

В том же окне до первого запуска добавим три временные переменные первичной инициализации (bootstrap), работа которых при старте описана в backend/open_webui/main.py и backend/open_webui/utils/auth.py:

  • WEBUI_ADMIN_EMAIL: ваш личный адрес электронной почты для входа; вводится приватно.
  • WEBUI_ADMIN_PASSWORD: ваш уникальный сильный пароль администратора; вводится приватно.
  • WEBUI_ADMIN_NAME: отображаемое имя владельца в интерфейсе.

Механизм bootstrap срабатывает только в пустой базе данных: функция create_admin_user проверяет наличие любых существующих пользователей и, если таблица пуста, создаёт первого администратора с указанным паролем, после чего в main.py принудительно сохраняет настройку ui.enable_signup = False. Если пользователи в базе уже есть, эти переменные ничего не меняют и существующий пароль не сбрасывают.

Dockhost и формат dockhost.yaml поддерживают подстановку глобальных переменных вида $GLOBAL_VARIABLE. Однако обычные поля окружения не стоит считать защищённым хранилищем с гарантированной маскировкой. WEBUI_SECRET_KEY, email и пароль вводим приватно в панели управления. Их реальные значения никогда не публикуем в Git, общих YAML-файлах, скриншотах или пересылаемых журналах.

Перед стартом проверим: диск смонтирован точно в /app/backend/data, задана одна реплика и один процесс UVICORN_WORKERS=1, все девять переменных заполнены, а публичного маршрута к сервису ещё нет. Запустим контейнер кнопкой «Применить». Если образ не скачивается, проверьте отсутствие опечаток в строке ghcr.io/open-webui/open-webui:v0.12.1 и доступность реестра GHCR, не подменяя официальный образ случайными сторонними сборками. Переход контейнера в статус Running сам по себе ещё не доказывает готовность базы и создание администратора, поэтому проверим оба результата внутри платформы.

Проверим запуск до открытия публичного маршрута

До публикации проверим отдельно создание владельца в базе данных и готовность веб-сервера. Ни одна из этих проверок не заменяет другую.

Сначала проверим создание администратора. Откройте в проекте раздел «Контейнеры», выберите контейнер open-webui и перейдите на вкладку «Логи» согласно документации по журналам контейнера. При первом чистом старте с заданными bootstrap-переменными код utils/auth.py в версии v0.12.1 выводит два разных сообщения: перед обращением к базе появляется строка Creating admin account..., и только после успешного хеширования пароля и вставки записи с ролью admin печатается подтверждение с префиксом:

Admin account created successfully:

Сразу после двоеточия в логе выводится ваш email, поэтому не копируйте и не публикуйте эту строку целиком. Само по себе сообщение Creating admin account... подтверждением успеха не является. Если при первом старте на новом диске итоговой строки Admin account created successfully: в журнале нет или видны ошибки миграции базы данных, не открывайте публичный маршрут и не пытайтесь зарегистрироваться через браузер вручную. Проверьте правильность имён переменных WEBUI_ADMIN_EMAIL и WEBUI_ADMIN_PASSWORD, точку монтирования /app/backend/data и то, что выбранный диск действительно был пустым. При повторных запусках на заполненной базе эта строка появляться не обязана: учётная запись уже существует.

Когда создание владельца подтверждено в логах, переключитесь в том же контейнере на вкладку «Терминал». По справке по консоли контейнера эта вкладка открывает командную оболочку внутри самой работающей реплики, поэтому локальный адрес 127.0.0.1:8080 обращается напрямую к процессу Open WebUI без участия внешних маршрутов. Утилита curl уже установлена в официальном образе (Dockerfile v0.12.1). Выполните в терминале контейнера команду проверки готовности:

curl --fail --silent --show-error --max-time 10 http://127.0.0.1:8080/ready

Команда не выводит переменные окружения, ключи или содержимое таблиц. В отличие от базового эндпоинта /health, который возвращает статический ответ, обработчик /ready в backend/open_webui/main.py возвращает HTTP 200 и JSON {"status":true} только после завершения startup_complete и успешного пинга базы данных SQLite. В проверку входит и Redis, если он настроен. Если запуск ещё не завершён или база недоступна, команда вернёт ошибку 503.

Успешный ответ /ready подтверждает, что процесс запущен и видит базу данных в текущий момент, но сам по себе не доказывает ни наличие прав администратора, ни работу внешнего HTTPS-маршрута, ни подключение языковой модели, ни сохранность диска при пересоздании контейнера. Убедившись, что в логах первого старта есть Admin account created successfully:, а /ready возвращает {"status":true}, переходим к публикации интерфейса.

Откроем HTTPS и войдём в свой аккаунт

У домена, сервиса и маршрута разные задачи. Домен задаёт имя сайта в браузере. Внутренний сетевой сервис объединяет живые реплики контейнера, а маршрут соединяет домен с нужным портом сервиса. Сам по себе открытый в контейнере порт 8080/TCP доступен только внутри проекта, пока мы явно не создадим правило маршрутизации.

Откроем «Сетевые сервисы» → «Маршруты» и создадим маршрут по инструкции настройки маршрутов:

  • Домен: выберите подготовленный в первом разделе домен.
  • Путь: / (корневой путь без перезаписи URL / rewrite).
  • Сервис: open-webui (автоматически созданный сервис нашего контейнера).
  • Порт: 8080 (8080/TCP).

Дождитесь выпуска SSL-сертификата и откройте свой фактический адрес https://ваш-домен в браузере. Если вы использовали собственный домен, предварительно должно пройти обновление записей DNS. Не обходите предупреждения браузера о недоверенном сертификате: при корректно выпущенном сертификате соединение сразу открывается по защищённому протоколу HTTPS.

На экране должна появиться форма входа в систему без кнопки публичной регистрации новых пользователей. Введите email и пароль, которые вы передавали в WEBUI_ADMIN_EMAIL и WEBUI_ADMIN_PASSWORD. В отличие от сценария, где первый администратор регистрируется вручную через веб-форму, здесь ваш профиль уже создан механизмом bootstrap: после входа убедитесь, что учётная запись принадлежит вам и в меню доступна панель администратора, а регистрация посторонних закрыта. Если браузер неожиданно предложил форму создания первого администратора с нуля, ничего в ней не заполняйте: немедленно проверьте, к тому ли сервису ведёт маршрут, смонтирован ли диск в /app/backend/data и почему не отработал bootstrap.

Многие параметры Open WebUI (ConfigVar), включая настройки доступа, после первого запуска сохраняются в базу данных. Как объясняет документация по переменным окружения, последующее изменение переменных контейнера не всегда перезаписывает настройки, сохранённые в панели администратора. При этом не отключайте переменную ENABLE_PERSISTENT_CONFIG: в режиме False приложение перестанет сохранять изменения настроек из веб-интерфейса.

Сразу после первого входа учётные переменные WEBUI_ADMIN_EMAIL, WEBUI_ADMIN_PASSWORD и WEBUI_ADMIN_NAME свою задачу выполнили. Удалим их при проверке перезапуска в последнем разделе. Сейчас форму редактирования открывать не будем. Успешный вход подтверждает работу веб-интерфейса и авторизации, а для генерации ответов в чате контейнеру нужно соединение с внешним API модели.

Проверим отдельное подключение модели

Когда вы открываете чат в браузере, запросы идут по двум разным участкам сети: ваш браузер общается с контейнером Open WebUI в Dockhost по HTTPS, а сам контейнер со своей площадки обращается к серверу языковой модели. Поэтому внутри контейнера в Dockhost адрес localhost или 127.0.0.1 указывает на сам этот контейнер, а не на ваш домашний компьютер с локальной моделью.

Подключите в настройках администратора внешний совместимый API-сервер по шагам из опубликованного урока про подключение OpenAI-совместимого API. В нашем сценарии достаточно убедиться в трёх вещах:

  1. URL выбранного поставщика API доступен по сети с серверов платформы Dockhost.
  2. Вы используете собственный действующий API-ключ с разрешённым доступом к выбранной модели (не вставляйте его в текст чата или публичные заметки).
  3. На балансе провайдера модели есть средства, если выбранный тариф платный.

Выберите модель в списке и отправьте короткое проверочное сообщение без личных, корпоративных или секретных данных. Например, попросите в двух предложениях объяснить, что такое сетевой порт. Каждый такой тестовый запрос может расходовать бюджет у поставщика API.

Оцените два результата в браузере:

  • Потоковая передача ответа. При включённом потоковом режиме текст ответа должен появляться на экране постепенно, по мере генерации, а не целиком после долгой паузы.
  • Работа WebSocket. Откройте инструменты разработчика в браузере (вкладка Network, фильтр WS) и обновите страницу чата: если клиент устанавливает WebSocket-соединение, в списке должен появиться запрос со статусом 101 Switching Protocols.

В документации Open WebUI по настройке HTTPS и обратного прокси указано, что для корректной работы чата прокси-сервер должен пропускать заголовки Upgrade и Connection для WebSocket, не буферизовать потоковые ответы (SSE) и иметь достаточные таймауты чтения. Если список моделей не загружается или возвращаются ошибки 401/403, причина обычно в ключе, URL или правах у внешнего провайдера API, а не в диске или образе контейнера. Если же обычный вход работает, но соединение WS не переключается в 101 или ответы застревают из-за буферизации, сначала проверьте точное совпадение WEBUI_URL и CORS_ALLOW_ORIGIN с вашим https://ваш-домен. При признаках буферизации на стороне входного прокси или сбое WebSocket обратитесь в поддержку Dockhost. Не будем искать специальные переключатели, которых нет в документации.

Если внешний API-ключ пока не подключён, вы уже проверили развёртывание интерфейса и защиту учётной записи, но проверка модели и потоковой генерации в вашем проекте остаётся невыполненной до настройки поставщика. Осталось убрать временные учётные данные из конфигурации контейнера и убедиться, что перезапуск не превращает установку в чистый пустой экземпляр.

Повторим вход после штатного перезапуска

Диск в /app/backend/data хранит таблицы базы данных, а неизменный WEBUI_SECRET_KEY позволяет новому процессу контейнера распознавать ранее выданные подписи и зашифрованные поля. Теперь мы удалим стартовые переменные администратора и проверим, что после перезапуска контейнера вход в ваш аккаунт и сохранённые данные остаются на месте.

Перед изменением настроек проверим, что в конфигурации контейнера open-webui остались прежними четыре элемента: имя open-webui, образ ghcr.io/open-webui/open-webui:v0.12.1, SSD-диск open-webui-data в точке /app/backend/data и значение WEBUI_SECRET_KEY. Сохраняем и остальные пять постоянных переменных: UVICORN_WORKERS, WEBUI_AUTH, ENABLE_SIGNUP, WEBUI_URL, CORS_ALLOW_ORIGIN. Секретные значения в открытый текст не выписываем.

Откройте редактирование контейнера в веб-интерфейсе Dockhost и удалите три переменные первичной инициализации:

  • WEBUI_ADMIN_EMAIL
  • WEBUI_ADMIN_PASSWORD
  • WEBUI_ADMIN_NAME

Сохраните изменения конфигурации контейнера. При перезапуске реплики после применения настроек или штатном перезапуске через интерфейс проекта для диска RWO прежняя реплика сначала останавливается, а новая подключает тот же самый том open-webui-data. Если при сохранении изменений платформа уже перезапустила контейнер с обновлённым набором переменных, второй раз перезапускать его вручную не требуется. Не увеличивайте число реплик, не отключайте диск и не создавайте вместо этого новый контейнер с другим томом.

Устанавливать консольную утилиту ради этой операции не нужно. Если на вашем компьютере уже установлен и авторизован Dockhost CLI, можно использовать команду из справки container restart. Выполняем её локально, заменив YOUR_PROJECT точным именем своего проекта. Вкладка «Терминал» внутри контейнера для этой команды не подходит:

dockhost container restart open-webui --project YOUR_PROJECT

После запуска новой реплики снова откройте вкладку «Терминал» контейнера и повторите проверку curl --fail --silent --show-error --max-time 10 http://127.0.0.1:8080/ready, дождавшись ответа {"status":true}. В логах повторного старта строка Admin account created successfully: появляться уже не должна, так как переменные удалены, а база данных не пуста.

Откройте https://ваш-домен в браузере и проверьте состояние установки:

  1. Ваш прежний логин и пароль администратора подходят для входа, роль владельца сохранена, а публичная регистрация остаётся закрытой.
  2. Если в предыдущем разделе вы провели тестовый диалог с моделью, этот чат отображается в истории.
  3. Если после перезапуска вдруг открылась страница первичной регистрации или пустая база, немедленно остановитесь и не регистрируйте нового пользователя: поскольку переменные bootstrap удалены, появление пустой базы требует проверить прежний диск и путь /app/backend/data, настройки базы и направление маршрута.

Если результат отличается от ожидаемого, проверим возможные причины:

  • Открылась чистая установка или пропали пользователи: проверьте, тот ли диск подключён, точен ли путь /app/backend/data и на тот ли сервис указывает маршрут.
  • Сбросилась активная сессия или перестали читаться защищённые настройки: проверьте, не изменилось ли значение WEBUI_SECRET_KEY.
  • В логах появились ошибки database is locked или сбои WAL: проверьте, что заданы одна реплика и UVICORN_WORKERS=1, а выбранное хранилище поддерживает блокировки SQLite.
  • Контейнер не переходит в готовность: изучите вкладку «Логи» и проверьте лимиты ресурсов проекта.
  • Интерфейс открывается, но модель не отвечает: проверьте настройки и баланс внешнего API-провайдера отдельно от контейнера.

Успешный повторный вход и прежний чат показывают, что проверенные данные доступны после этого перезапуска. Неизменность диска и WEBUI_SECRET_KEY проверяем отдельно в конфигурации: один вход не доказывает сохранность всех данных и ключей. Обычный рестарт также не подтверждает высокую доступность (HA), сохранность данных при удалении диска или пересоздании проекта с нуля и не заменяет тестовое восстановление из резервной копии.

Перед обновлением образа сохраним отдельную резервную копию всех данных из /app/backend/data и свой WEBUI_SECRET_KEY. Новый релиз может изменить схему базы. Возврат к тегу v0.12.1 не гарантирует откат этих изменений.

Проверки лога создания администратора, /ready, входа по HTTPS, потокового ответа модели, отдельного WebSocket-соединения со статусом 101 и прежнего аккаунта после перезапуска без bootstrap-переменных относятся к разным частям установки. Если внешний API или пригодность хранилища у оператора ещё не проверены, эти шаги остаются незавершёнными. До их проверки постоянные рабочие диалоги сюда не переносим.

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

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

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

stro-island>