Оригинал: https://timofei-ermolaev.ru/courses/open-webui/drugie-sposoby/open-webui-na-servere/

# Open WebUI на удалённом Linux-сервере

Поможем открыть Open WebUI на удалённом Linux-сервере через SSH-туннель и разобраться, как связаны доступ, работа контейнера и сохранение данных.

[![Тимофей Ермолаев](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)

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

18 октября 2026 г.

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

18 октября 2026 г.

**На урок**

20 мин

**Тема**

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

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

- Различать адреса своего компьютера, серверного хоста и контейнера
- Открывать существующий серверный экземпляр через локальный SSH-туннель
- Отделять время жизни туннеля от работы Docker и сохранения данных
- Проверять доступ, готовность приложения и подключение модели отдельно

**Проверяемый результат:** Понятно, как открыть свой серверный Open WebUI, где выполнять команды и что изменится после закрытия SSH-туннеля.

Почему Open WebUI отвечает на сервере, а тот же `localhost` в моём браузере его не открывает? Когда браузер и контейнер работают на разных машинах, адрес `127.0.0.1` в адресной строке ведёт к вашему собственному компьютеру, а не к серверу. Чтобы открыть уже работающий серверный экземпляр и не публиковать его порт в интернет, проложим между двумя компьютерами личный SSH-туннель.

Для урока нужен свой сервер Ubuntu 24.04, к которому у вас есть разрешённый и проверенный доступ по SSH. Для вашей учётной записи должно быть разрешено локальное перенаправление портов (local TCP forwarding), а в папке `~/projects/open-webui` уже должен работать проект из предыдущих материалов. На удалённом сервере контейнер работает на другой машине. Способы запуска через Docker или Python и отдельное подключение языковых моделей остаются прежними, как описано в [руководстве по развёртыванию Open WebUI](<https://docs.openwebui.com/deployment/>) .

Если до этого приложение работало только на вашем ноутбуке, на удалённый хост оно автоматически не перенеслось. На новом сервере сначала пройдём [подготовку Docker для Linux](<https://timofei-ermolaev.ru/courses/open-webui/install/podgotovka-docker-dlya-linux/>) и развернём проект по уроку [установки Open WebUI через Docker Compose](<https://timofei-ermolaev.ru/courses/open-webui/install/docker-compose/>) прямо в серверном SSH-терминале, а затем вернёмся сюда перед открытием браузера.

## Проверим работу Open WebUI на сервере

Сначала убедимся, что на сервере работает нужный контейнер и приложение готово отвечать на запросы. Затем настроим путь к нему со своего компьютера. Войдём на сервер под своим обычным пользователем с теми же параметрами порта и SSH-ключа, которые вы уже проверили раньше. Как описано в [руководстве `ssh(1)`](<https://man.openbsd.org/ssh.1>) и [справочнике `ssh_config(5)`](<https://man.openbsd.org/ssh_config.5>) , при первом подключении или при предупреждении о смене ключа сверяем отпечаток (fingerprint) с доверенными данными от администратора или из панели провайдера. Отключать проверку ключа флагом `StrictHostKeyChecking=no` или удалять записи наугад не будем.

**На своём компьютере** откроем терминал и подключимся к серверу (замените `courseuser` и `server.example.com` на имя пользователя и адрес вашего сервера):

```
ssh  courseuser@server.example.com  
```

После входа перейдём в существующую папку проекта и посмотрим список его контейнеров. Если в подготовительном уроке вы обращались к Docker через `sudo`, сохраним этот способ и здесь.

**На сервере** проверим каталог и статус контейнера через [`docker compose ps`](<https://docs.docker.com/reference/cli/docker/compose/ps/>) :

```
cd  ~/projects/open-webui  
pwd  
sudo  docker  compose  ps  --all  
```

Флаг `--all` показывает все контейнеры проекта, включая остановленные. Команда `pwd` должна вывести полный путь к папке `~/projects/open-webui`, а `sudo docker compose ps --all` показать запущенный сервис `open-webui` с привязкой `127.0.0.1:3000->8080/tcp` в колонке портов. Если каталог не найден, список пуст, контейнер остановлен или порты отличаются, прервёмся и проверим, на тот ли сервер и в тот ли проект мы вошли. Создавать новую папку или запускать `docker compose up` поверх неизвестного состояния не нужно.

Теперь проверим, готово ли приложение отвечать по HTTP на самом сервере. В исходном коде [Open WebUI 0.12.3 (commit `91ea4e34`)](<https://github.com/open-webui/open-webui/commit/91ea4e3421d1d8069f13d4a463fcb5986d94ea60>) в файле [`backend/open_webui/main.py`](<https://raw.githubusercontent.com/open-webui/open-webui/91ea4e3421d1d8069f13d4a463fcb5986d94ea60/backend/open_webui/main.py>) маршрут `/ready` проверяет завершение старта приложения, выполняет запрос `SELECT 1` к базе данных и проверяет Redis, если он настроен. Этот маршрут подтверждён для версии 0.12.3; если у вас установлена другая версия, сначала сверьтесь с её документацией и не обновляйте образ специально ради этой проверки. Обычный маршрут `/health` в этом коде всегда возвращает только статический ответ, а запрос к произвольному неизвестному адресу может вернуть HTML-оболочку интерфейса с кодом `200`, поэтому для проверки готовности они не подходят.

**На сервере** запросим маршрут `/ready` с помощью [`curl`](<https://curl.se/docs/manpage.html>) :

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

Флаг `--fail` заставляет `curl` завершаться с ошибкой при HTTP-кодах вроде `503`. Пара `--silent` и `--show-error` скрывает индикатор загрузки, но выводит текст ошибки при сбое. Флаг `--max-time 10` ограничивает ожидание десятью секундами. Для готового экземпляра поддерживаемой версии ожидаем успешный ответ JSON со значением `status: true`. Проверка подтверждает готовность веб-приложения и его базы данных на сервере. Языковую модель она не вызывает.

## Свяжем свой компьютер с сервером через SSH-туннель

Когда приложение отвечает на сервере, разберёмся, почему одинаковый адрес `127.0.0.1` ведёт в разные места. Loopback-интерфейс всегда указывает на то устройство, где выполняется запрос. Чтобы различать локальный вход туннеля и серверный порт, выберем на своём компьютере свободный порт `13000`. Путь запроса пройдёт через три точки:

| Где | Адрес/порт | Роль |
| --- | --- | --- |
| Компьютер с браузером | `127.0.0.1:13000` | Локальный вход SSH-туннеля |
| Удалённый Linux-хост | `127.0.0.1:3000` | Опубликованный порт прежнего Compose |
| Контейнер Open WebUI | `8080` | Порт приложения внутри контейнера |

Проследим путь запроса целиком. Браузер на вашем компьютере обращается к локальному адресу `127.0.0.1:13000`. Запущенный там же клиент OpenSSH принимает это соединение, переносит его по зашифрованному SSH-каналу на удалённый сервер и со стороны сервера обращается к `127.0.0.1:3000`. Правило публикации портов из нашего `compose.yaml` в стандартной bridge/NAT-сети передаёт трафик с серверного порта `3000` на порт `8080` внутри контейнера.

Поскольку на сервере порт привязан к `127.0.0.1`, открывать порт `3000` в панели управления облачного провайдера или в межсетевом экране не требуется. В [документации Docker по публикации портов](<https://docs.docker.com/engine/network/port-publishing/>) отмечено, что актуальный Docker Engine включает исправление из версии 28.0.0, тогда как в более старых выпусках порты на `localhost` могли оставаться достижимыми из того же сегмента локальной сети (L2). Привязка к loopback-адресу также не изолирует порт от других пользователей, у которых уже есть доступ к этому серверному хосту.

Для запуска туннеля откроем второе окно терминала на том же компьютере, где вы будете открывать браузер. Вводить эту команду внутри уже открытой серверной SSH-сессии не нужно. Номер `13000` выбран как свободный локальный порт на вашем компьютере; серверный порт `3000` и контейнерный `8080` остаются без изменений. Если при обычном входе вы указывали нестандартный порт SSH (`-p`) или путь к ключу (`-i`), добавьте эти же флаги в команду.

**На своём компьютере** запустим перенаправление портов в новом локальном терминале:

```
ssh  -N  -o  ExitOnForwardFailure=yes  -L  127.0.0.1:13000:127.0.0.1:3000  courseuser@server.example.com  
```

Разберём параметры по документации [`ssh(1)`](<https://man.openbsd.org/ssh.1>) и [`ssh_config(5)`](<https://man.openbsd.org/ssh_config.5>) :

- **`-N`** указывает клиенту не запускать командную оболочку на сервере, так как это соединение нужно нам только для пересылки трафика.
- **`-L 127.0.0.1:13000:127.0.0.1:3000`** открывает порт `13000` только на локальном адресе вашего компьютера и перенаправляет входящие соединения на адрес `127.0.0.1:3000` со стороны сервера.
- **`-o ExitOnForwardFailure=yes`** завершает работу SSH-клиента с ошибкой, если ему не удалось занять локальный порт или согласовать правило перенаправления. При этом сам параметр проверяет только создание туннеля, а не работоспособность контейнера Open WebUI, и не гарантирует закрытие клиента, если приложение на сервере остановится позже.

В нашем учебном сценарии используется обычный запуск SSH на переднем плане (foreground) без заранее настроенных фоновых мультиплексоров `ControlMaster` и `ControlPersist`. После успешного подключения команда может ничего не выводить и не возвращать приглашение ввода. Оставим это окно терминала открытым. По [документации `sshd_config(5)`](<https://man.openbsd.org/sshd_config.5>) , разрешение на перенаправление портов (`AllowTcpForwarding`) настраивается на сервере отдельно от права на вход в терминал. Если вход в оболочку работает, а туннель запрещён политикой сервера, обратитесь к администратору; настройки демона `sshd` в этом уроке мы не меняем.

## Откроем серверный интерфейс в своём браузере

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

**На своём компьютере** откроем адрес в браузере:

```
http://127.0.0.1:13000  
```

Ожидаем увидеть знакомую страницу входа в Open WebUI и войти под своей существующей учётной записью. Порядок регистрации и базовые настройки разобраны в уроке про [первый вход администратора](<https://timofei-ermolaev.ru/courses/open-webui/configure/first-admin/>) . Повторять проверку через `curl` на локальном компьютере не требуется. Если же вместо окна входа браузер предлагает создать первого администратора с нуля, сразу остановитесь и ничего не заполняйте: это признак того, что вы подключились к другому экземпляру или контейнер запустился с пустым томом данных.

Различим четыре проверки:

1. Работающий статус контейнера в Docker показывает, что процесс запущен.
2. Успешный ответ `/ready` на сервере подтверждает, что приложение прошло инициализацию и видит свою базу данных.
3. Открытие страницы проверяет путь через туннель, а вход в знакомую учётную запись помогает убедиться, что мы попали в нужный экземпляр. Сам по себе вход не подтверждает, что секретный ключ остался прежним.
4. Ответ языковой модели в чате проверяет отдельное соединение между сервером Open WebUI и сервером моделей. В этом уроке мы не вызываем модель. Открытие интерфейса в браузере ноутбука не переносит на него вычисления (инференс). Успешный вход в аккаунт также не проверяет полноту архивной истории или восстановление из резервной копии.

## Что продолжит работать после закрытия туннеля

Серверный Open WebUI продолжит работать после закрытия локального SSH-клиента. Окно туннеля нужно для доступа из браузера; контейнером на сервере управляет Docker.

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

**На своём компьютере** завершим работу foreground-туннеля сочетанием клавиш:

```
Ctrl+C  
```

После нажатия `Ctrl+C` клиент OpenSSH закроет соединение, и новые запросы из браузера к `http://127.0.0.1:13000` перестанут доходить до сервера. Открытая ранее вкладка может оставаться на экране из памяти браузера. Видимая страница сама по себе не означает, что связь с сервером ещё активна.

Когда вам снова понадобится открыть интерфейс, переустанавливать проект или перезапускать контейнер не придётся.

**На своём компьютере** достаточно повторить запуск туннеля и снова открыть адрес в браузере:

```
ssh  -N  -o  ExitOnForwardFailure=yes  -L  127.0.0.1:13000:127.0.0.1:3000  courseuser@server.example.com  
```

На самом сервере контейнер работает в фоновом режиме (`detached`) под управлением системной службы Docker Engine. Как объясняется в [документации Docker по автозапуску контейнеров](<https://docs.docker.com/engine/containers/start-containers-automatically/>) и в [руководстве по послеустановочной настройке Linux](<https://docs.docker.com/engine/install/linux-postinstall/>) , постоянная работа сервиса складывается из двух условий: сама служба `docker` включена в автозагрузку Ubuntu через `systemd`, а для контейнера в `compose.yaml` задана политика `restart: unless-stopped`. Эта политика перезапускает контейнер при сбое или после перезапуска демона Docker. Если вы сами остановили проект командой `stop`, автоматически он не включится. Политика перезапуска не проверяет внутреннюю готовность приложения и не заменяет резервное копирование. Писать поверх Docker отдельный файл службы `systemd` для контейнера или настраивать пользовательский `linger` (который применяется в других сценариях, вроде пользовательских служб Python или Podman) здесь не нужно.

Сохранность ваших чатов, настроек и учётных записей тоже не зависит от SSH-туннеля. Как указано в [руководстве быстрого старта](<https://docs.openwebui.com/getting-started/quick-start/>) и в [рекомендациях по укреплению безопасности Open WebUI](<https://docs.openwebui.com/getting-started/advanced-topics/hardening/>) , данные хранятся внутри контейнера по пути `/app/backend/data`, куда подключён постоянный том Docker, а сессии опираются на сохранённый `WEBUI_SECRET_KEY`. Сохраним прежние проект, том и значение этого ключа; сам том не заменяет резервную копию. Папка `~/projects/open-webui` с файлом `compose.yaml` содержит только конфигурацию запуска, тогда как сами данные лежат в томе под управлением Docker.

Если вам нужно убедиться, что служба Docker на сервере активна и добавлена в автозагрузку, это можно проверить стандартными командами `systemd`.

**На сервере** (при необходимости проверки службы движка):

```
systemctl  is-active  docker  
systemctl  is-enabled  docker  
```

В ожидаемом выводе первая команда вернёт `active`, а вторая `enabled`. Чтобы проверить политику перезапуска и подключённое хранилище контейнера без вывода секретных переменных окружения, воспользуемся точечным форматированием [`docker inspect`](<https://docs.docker.com/reference/cli/docker/inspect/>) .

**На сервере** (при необходимости проверить настройки перезапуска и том контейнера) сначала узнаем идентификатор контейнера, а затем подставим полученную строку вместо `CONTAINER_ID`:

```
sudo  docker  compose  ps  -q  open-webui  
sudo  docker  inspect  --type  container  --format  '{{.HostConfig.RestartPolicy.Name}}'  CONTAINER_ID  
sudo  docker  inspect  --type  container  --format  '{{range .Mounts}}{{println .Type .Name .Destination}}{{end}}'  CONTAINER_ID  
```

Первая команда с флагом `-q` выведет идентификатор работающего контейнера сервиса. Во второй команде ожидаем увидеть политику `unless-stopped`, а в третьей строку с типом `volume`, именем прежнего тома и точкой назначения `/app/backend/data`. Имя зависит от проекта; сверяем его со своей прежней конфигурацией. Если контейнер остановлен, сначала вернёмся к выводу `sudo docker compose ps --all`.

## Разберём ошибки подключения

Если интерфейс не открылся, начните с первого неподтверждённого звена в цепочке. Переустанавливать Docker или пересоздавать проект не нужно:

- **Не удаётся войти по SSH или появилось предупреждение о смене ключа хоста.** Проверьте адрес сервера, имя пользователя, порт и ключ, которыми вы пользовались раньше. Если терминал сообщает об изменении ключа сервера (`REMOTE HOST IDENTIFICATION HAS CHANGED`), остановитесь и сверьте отпечаток ключа через консоль провайдера или у администратора. Не обходите эту проверку и не переустанавливайте Open WebUI: ошибка возникла ещё до обращения к контейнеру.
- **При запуске туннеля появляется `Address already in use`.** Это значит, что локальный порт `13000` на вашем компьютере уже занят другим окном терминала со старым туннелем или другой программой. Закройте дублирующий туннель или выберите другой свободный локальный порт (например, замените `13000` на `13001` в левой части флага `-L` и открывайте `http://127.0.0.1:13001` в браузере). Менять порт `3000` в серверном `compose.yaml` из-за локального конфликта не нужно.
- **В терминале появляется `administratively prohibited` или отказ в создании forward.** Ваша учётная запись или конфигурация SSH-сервера запрещает перенаправление TCP-портов, даже если обычный вход в командную строку работает. Этот вопрос решается с администратором сервера.
- **Команда `curl` на сервере не возвращает успех на `/ready` или в окне туннеля мелькает `channel open failed: connect failed`.** Успешный запуск SSH-клиента не означает, что само приложение готово. Если контейнер только что запустился, подождите завершения инициализации и повторите проверку `curl`. Если ошибка сохраняется, проверьте по документации установленной версии, поддерживает ли она маршрут `/ready`, и обратитесь к журналу контейнера по предыдущему уроку. Плавающий тег вроде `ghcr.io/open-webui/open-webui:main` сам по себе не доказывает точный номер установленного релиза.

**На сервере** (если при диагностике нужно проверить контекст и версии Docker или повторить запрос готовности):

```
sudo  docker  version  
sudo  docker  compose  version  
sudo  docker  context  ls  
curl  --fail  --silent  --show-error  --max-time  10  http://127.0.0.1:3000/ready  
```

- **На сервере `/ready` отвечает успешно, а в браузере страница не открывается.** Проверьте, что второе локальное окно терминала с командой `ssh -N ...` остаётся открытым на том же компьютере, где запущен браузер, и что в адресной строке указан именно локальный порт туннеля `http://127.0.0.1:13000`, а не `3000` и не `https://`.
- **Вместо входа открылась регистрация первого администратора.** Остановитесь, не создавайте новый аккаунт и проверьте по шагам выше, в той ли папке проекта на сервере запущен контейнер и какой том примонтирован в `/app/backend/data`.
- **Интерфейс открылся, вы вошли в аккаунт, но модель не отвечает.** Ваш личный доступ к Open WebUI через SSH работает исправно. Доступность сервера моделей настраивается и диагностируется отдельно от веб-интерфейса.

Личный доступ одного администратора или разработчика устроен так: серверный контейнер и его хранилище остаются на месте, а интерфейс вы открываете по адресу `http://127.0.0.1:13000` через зашифрованный SSH-туннель. Как отмечается в документации по [планированию развёртывания](<https://docs.openwebui.com/deployment/>) , [сетевой защите](<https://docs.openwebui.com/getting-started/advanced-topics/hardening/>) и [настройке HTTPS с обратным прокси](<https://docs.openwebui.com/reference/https/>) , для совместной работы команды или доступа через собственный домен потребуются отдельные решения по TLS-сертификатам, аутентификации, резервному копированию и мониторингу.

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

- [Подготовка Docker для Linux timofei-ermolaev.ru](<https://timofei-ermolaev.ru/courses/open-webui/install/podgotovka-docker-dlya-linux/>)
- [Установка Open WebUI через Docker Compose timofei-ermolaev.ru](<https://timofei-ermolaev.ru/courses/open-webui/install/docker-compose/>)
- [Первый вход и базовые настройки доступа timofei-ermolaev.ru](<https://timofei-ermolaev.ru/courses/open-webui/configure/first-admin/>)

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

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

Урок пройден

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

## Об авторе

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

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

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

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