MAATRIX / Блог / Как установить и настроить Open WebUI на VPS

Как установить и настроить Open WebUI на VPS

Как установить и настроить Open WebUI на VPS

MAATRIX

Open WebUI даёт привычный интерфейс чата поверх ваших моделей и внешних API, но на ноутбуке от него мало толку: закрыли крышку — сервис пропал, а ключи разъехались по устройствам. Разберём установку Open WebUI на VPS до рабочего сервиса: домен, HTTPS, живой стриминг, бэкап и обновление — с командами и перечнем мест, где эта конструкция ломается.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Что делает Open WebUI и почему ему нужен отдельный VPS

Open WebUI — веб-интерфейс, который сам не считает ни одного токена. Он разговаривает с нативным API Ollama и с любым OpenAI-совместимым сервисом: самим OpenAI, OpenRouter, vLLM, локальным шлюзом. Взамен — аккаунты, история переписки в базе, документы с поиском по ним, веб-поиск и права доступа к моделям.

Это стоит понять до установки: половина разочарований звучит как «поставил Open WebUI, а он тормозит». Тормозит не он, а модель на процессоре или канал до внешнего API.

Отдельный сервер даёт три вещи: доступность круглосуточно с единой историей на всех устройствах, ключи в одном месте (коллеге выдаётся доступ к модели, а не сам ключ) и постоянный «чистый» IP — зарубежные API спокойно отвечают из Лондона и заметно менее спокойно из российских подсетей и с адресов публичных VPN.

Готовим сервер: ОС, Docker и порты

Берите Ubuntu 24.04 LTS или Debian 12. Docker — так, а не через apt install docker.io: в дистрибутивном пакете нет плагина docker compose.

curl -fsSL https://get.docker.com | sudo sh
sudo systemctl enable --now docker && docker compose version
sudo ufw allow 22/tcp && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw enable

А теперь ловушка, из-за которой чужие Open WebUI находят сканированием 3000 порта. Docker не подчиняется ufw. Когда вы пишете -p 3000:8080, правило уходит в цепочку DOCKER-USER, которая обрабатывается раньше INPUT, — порт открыт в интернет, даже если ufw его запрещает. Лечение одно: публиковать порт на петлю, -p 127.0.0.1:3000:8080, и проверять результат.

ss -tlnp | grep 3000
LISTEN 0  4096  127.0.0.1:3000  0.0.0.0:*  users:(("docker-proxy",pid=1442,fd=7))

Если вместо 127.0.0.1:3000 стоит 0.0.0.0:3000 — интерфейс открыт всему интернету по голому HTTP. Порт Ollama (11434) наружу не публикуется никогда: аутентификации у него нет.

По диску: распакованный образ занимает около 3,7 ГБ, пустая база — меньше 200 МБ. Без локальных моделей хватает 40–60 ГБ NVMe; если Ollama живёт здесь же, считайте по 5 ГБ на модель уровня 7–8B. Плюс swap на 2–4 ГБ — он спасёт контейнер от падения при обновлении.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Open WebUI

Установка Open WebUI: три способа и какой выбрать

СпособКому подходитМинусы
docker composeпродакшен, связка с Ollamaодин файл, зато воспроизводимо
pip install open-webuiгде Docker запрещёнPython 3.11, venv 5–6 ГБ, systemd-юнит свой
образ :ollamaвсё в одном~10 ГБ, обновление тянет за собой Ollama

Рабочий вариант — /opt/openwebui/docker-compose.yml:

services:
  ollama:
    image: ollama/ollama:latest
    restart: unless-stopped
    volumes: [ollama:/root/.ollama]
    environment:
      - OLLAMA_KEEP_ALIVE=30m

  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    restart: unless-stopped
    depends_on: [ollama]
    ports: ["127.0.0.1:3000:8080"]
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
      - WEBUI_SECRET_KEY=${WEBUI_SECRET_KEY}
      - WEBUI_URL=https://chat.example.com
      - ENABLE_SIGNUP=false
      - DEFAULT_USER_ROLE=pending
    volumes: [open-webui:/app/backend/data]

volumes:
  ollama:
  open-webui:
cd /opt/openwebui
echo "WEBUI_SECRET_KEY=$(openssl rand -hex 32)" > .env && chmod 600 .env
docker compose up -d

Первый запуск не мгновенный: контейнер докачивает модель эмбеддингов (all-MiniLM-L6-v2, около 90 МБ). На 2 vCPU это 40–90 секунд, и всё это время браузер видит пустую страницу или ошибку прокси — не чините то, что ещё не запустилось:

curl -s http://127.0.0.1:3000/health
{"status":true}

Не зададите WEBUI_SECRET_KEY — Open WebUI сгенерирует его сам в /app/backend/data/.webui_secret_key, и при переезде все сессии станут недействительными.

Адрес именно http://ollama:11434, а не http://localhost:11434: внутри контейнера localhost — это сам Open WebUI. Если Ollama стоит на хосте пакетом, разрешите ей слушать не только петлю (sudo systemctl edit ollama.service, строка Environment="OLLAMA_HOST=0.0.0.0:11434") и добавьте extra_hosts: ["host.docker.internal:host-gateway"].

Домен, HTTPS и стриминг: reverse proxy без сюрпризов

На голом IP по HTTP браузер не даст доступ к микрофону, а трафик с ключами пойдёт открытым текстом. Заводим A-запись chat.example.com, проверяем dig +short chat.example.com и ставим Caddy: он берёт сертификат Let's Encrypt сам и корректно проксирует WebSocket.

# /etc/caddy/Caddyfile
chat.example.com {
    reverse_proxy 127.0.0.1:3000
    request_body { max_size 200MB }
}

sudo systemctl reload caddy — через 10–20 секунд сайт открывается по HTTPS (разбор Caddy с авто-SSL). Если вы принципиально на Nginx, конфиг обязан содержать то, что по умолчанию настроено неправильно для Open WebUI:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_buffering off;
    proxy_read_timeout 600s;
}

Плюс client_max_body_size 200M; на уровне server. Что ломается без них:

  • Без Upgrade/Connection "upgrade" и proxy_buffering off ответ не печатается по словам: спиннер крутится, потом текст вываливается целиком.
  • Дефолтный proxy_read_timeout 60s рвёт длинную генерацию на полуслове. В /var/log/nginx/error.log появляется upstream timed out (110: Connection timed out) while reading response header from upstream.
  • Дефолтный client_max_body_size 1m ломает загрузку PDF в базу знаний: 413 Request Entity Too Large, а в интерфейсе видно только лаконичное «Upload failed» без причины.

Первый вход и настройки, которые надо сделать сразу

Открываем https://chat.example.com и регистрируемся. Первый аккаунт автоматически становится администратором, подтверждения почты нет. Регистрируйтесь сразу после старта контейнера: сервер с доменом и открытой формой сканеры находят за часы.

Дальше — самая дорогая по времени ловушка. Часть переменных окружения читается только при первом запуске, дальше значение живёт во внутренней базе (в документации это PersistentConfig). На практике: правите OLLAMA_BASE_URL в compose, делаете docker compose up -d, а в интерфейсе старый адрес — и полчаса ищете, где ошиблись. Нигде: значение просто игнорируется. Лечится правкой в «Админ-панель → Настройки → Подключения» либо переменной ENABLE_PERSISTENT_CONFIG=False.

Что настроить сразу:

  • Регистрация. ENABLE_SIGNUP=false закрывает публичную форму. Нужно пускать людей — оставьте её открытой с DEFAULT_USER_ROLE=pending: аккаунт создаётся, но до одобрения админом не видит ни одной модели.
  • Подключения и права. Внешние API добавляются через OPENAI_API_BASE_URL и OPENAI_API_KEY; несколько провайдеров перечисляются через ; в одинаковом порядке в обеих переменных. У каждой модели есть видимость и группы — дорогую внешнюю разумно открыть двум людям, иначе счёт станет сюрпризом.
  • Документы (RAG). По умолчанию работает локальная модель эмбеддингов, чанк 1000 символов с перекрытием 100, три фрагмента на запрос. Индексация PDF на сотню страниц на 2 vCPU занимает около полутора минут; если документов много — переключите движок на Ollama с nomic-embed-text.
  • Веб-поиск. ENABLE_WEB_SEARCH=true, движок в WEB_SEARCH_ENGINE (duckduckgo не требует ключа). Оговорка: до ветки 0.6 переменные назывались ENABLE_RAG_WEB_SEARCH и RAG_WEB_SEARCH_ENGINE — на старом образе работают старые имена, а новые молча игнорируются.

Модели тянет Ollama:

docker compose exec ollama ollama pull qwen2.5:7b
docker compose exec ollama ollama list
NAME              ID              SIZE      MODIFIED
qwen2.5:7b        845dbda0ea48    4.7 GB    2 minutes ago
nomic-embed-text  0a109f422b47    274 MB    5 minutes ago

В закрытом контуре добавьте OFFLINE_MODE=true: иначе на старте контейнер минутами ждёт таймаута при попытках достучаться до реестра моделей.

Обслуживание: бэкап, обновление и рост нагрузки

Всё состояние лежит в томе /app/backend/data: webui.db (SQLite), каталог uploads, векторная база и .webui_secret_key.

docker compose stop open-webui
docker run --rm -v openwebui_open-webui:/data -v /root/backups:/backup alpine \
  tar czf /backup/openwebui-$(date +%F).tar.gz -C /data .
docker compose start open-webui
docker compose pull && docker compose up -d && docker image prune -f

Точное имя тома смотрите в docker volume ls — Compose приписывает к нему имя каталога проекта. Архив на живой SQLite снять можно, но при неудачном тайминге получите базу в несогласованном состоянии и узнаете об этом при восстановлении.

На старте новая версия сама применяет миграции базы, и здесь нужна честность: обратных миграций нет. Сломалось после обновления — вернуть предыдущий тег обычно не выйдет: новая схема старым кодом не читается, восстанавливаться придётся из бэкапа. Порядок один: сначала архив, потом pull. И на боевом сервере лучше фиксировать версию, а не сидеть на плавающем :main.

Наблюдать проще через docker compose logs -f open-webui --tail=100 и docker stats --no-stream. В покое Open WebUI с локальными эмбеддингами держит около 1,0–1,3 ГБ резидентной памяти; отдадите эмбеддинги Ollama — расход падает до 500–600 МБ.

Про рост: SQLite держит примерно до десятка активных пользователей, дальше идут подвисания и записи database is locked в логах. Решение — Postgres через DATABASE_URL=postgresql://openwebui:пароль@db:5432/openwebui. Оговорка: официального инструмента переноса данных из SQLite в Postgres нет, так что если знаете про отдел пользователей заранее — поднимайте Postgres сразу.

Рядом: запуск моделей через Ollama и сколько ресурсов нужно VPS под нейросети.

Какой сервер взять в MAATRIX под Open WebUI

Конфигурация зависит от того, где считаются модели. Сценария два.

Сценарий А: Open WebUI как единый фронт к внешним API. Модели считает OpenAI, OpenRouter или ваш шлюз, сервер занимается интерфейсом, базой и индексацией документов. Реальный минимум — 2 vCPU, 4 ГБ RAM, 40–60 ГБ NVMe. На 2 ГБ контейнер тоже стартует и первые дни выглядит нормально, но при первой же индексации большого PDF уходит в OOM. Комфортно — 4 vCPU и 8 ГБ.

Сценарий Б: Open WebUI плюс Ollama на том же сервере, без GPU. Здесь всё определяет модель — она целиком лежит в оперативной памяти. 7–8B в квантовании Q4 — около 5 ГБ весов плюс контекст плюс сам интерфейс. Рабочая конфигурация: 6–8 vCPU, 12–16 ГБ RAM, от 100 ГБ NVMe. Честно о скорости: 8B в Q4 на процессоре выдаёт порядка 5–8 токенов в секунду при 8 vCPU — для личных задач терпимо, для пяти человек одновременно нет. Варианта три: модели поменьше (3–4B ощутимо бодрее), вынос тяжёлых запросов во внешний API или GPU. Диск берите NVMe: модель на 5 ГБ подгружается в память при первом обращении после выгрузки.

Локация. Для Open WebUI, который ходит во внешние API, мы обычно советуем Лондон: британские адреса принимаются зарубежными AI-сервисами без верификационных танцев, а пинг из Москвы держится в районе 45–60 мс против 110–130 мс до Нью-Йорка — для чата это разница между «мгновенно» и «заметно». Франция — равноценная альтернатива. США берут, когда нужен именно американский IP. Если пользователями будут только сотрудники в России и вы обрабатываете персональные данные — берите Россию: минимальный пинг и никаких вопросов по 152-ФЗ. Сравнение — в материале про VPS в Великобритании для доступа к нейросетям.

Оплата — картами российских банков, по СБП, криптовалютой или токеном MAAT; иностранная карта не нужна, даже когда сервер стоит в Лондоне. Не уверены, какой сценарий ваш, — напишите, сколько будет пользователей, и мы подберём конфигурацию под задачу.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Open WebUI

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Частые вопросы

Сколько нужно RAM, если модели крутятся во внешнем API?

4 ГБ — рабочий минимум, 8 ГБ — комфорт. Интерфейс в покое держит около 1,0–1,3 ГБ, остальное съедает индексация документов.

Почему ответы не печатаются по словам, а появляются целиком в конце?

Обратный прокси не пропускает WebSocket или буферизует ответ. В Nginx нужны Upgrade $http_upgrade, Connection "upgrade" и proxy_buffering off; Caddy делает это сам.

Я поменял переменную окружения, а Open WebUI её игнорирует. Почему?

Часть настроек читается только при первом запуске и дальше хранится в базе (PersistentConfig). Правьте значение в «Админ-панель → Настройки» или добавьте ENABLE_PERSISTENT_CONFIG=False.

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.