Самостоятельный хостинг
Flow поставляется контейнерами и поднимается через docker compose: в репозитории лежит
готовый файл infra/prod/docker-compose.yml и пример переменных окружения к нему
(infra/prod/.env.example). Это одна машина, на которой работает всё: приложение, база,
очередь, векторный поиск, изолированная песочница для кода и входной прокси.
Тарифные ограничения, о которых говорят остальные страницы документации, на своей установке
ваши. При первом старте в базу засеваются два плана — free и paid — но их числа
объявлены в коде как заготовки, и администратор инстанса правит их прямо в
админке. Так что «на бесплатном нельзя Telegram» — это решение того, кто
держит установку, а не свойство платформы.
Из чего состоит стек
Девять контейнеров. Их роли не взаимозаменяемы: три из них хранят состояние, два исполняют воркфлоу, два раздают интерфейс, один изолирует чужой код, один принимает трафик.
| Контейнер | Что это | Состояние |
|---|---|---|
| postgres | основная база: воркфлоу, запуски, история чатов, учёт кредитов | том postgres_data |
| redis | очередь запусков, кросс-процессная шина событий (SSE), рантайм-состояние | том redis_data |
| qdrant | векторное хранилище баз знаний | том qdrant_data |
| api | HTTP API и SSE; он же применяет миграции при старте | том app_data (файлы) |
| worker | исполнение воркфлоу из очереди — тот же образ, другая команда | том app_data (тот же) |
| js-executor | изолированная песочница для нод с кодом | нет |
| frontend | собранный SPA — сам редактор | нет |
| web | сайт: документация, примеры, страница публичного чата | нет |
| edge | nginx: единственный публичный вход, маршрутизация по домену | нет |
Три вещи в этой раскладке неочевидны, и все три ломаются молча.
api и worker — один и тот же образ. Отличаются командой запуска и тем, что миграции
применяет только api (RUN_MIGRATIONS=1): двух мигрирующих процессов быть не должно.
Движок при этом живёт в обоих — часть запусков (синхронный вызов, ход чата, агент как
инструмент, суб-воркфлоу) выполняется прямо в api.
Том с файлами обязан быть примонтирован в оба. app_data держит вложения сообщений,
сгенерированные ноды файлы и исходники документов баз знаний. Пишет их движок из worker, а
раздаёт api — при раздельных файловых системах скачивание отдаёт 404, а без тома вообще всё
пользовательское содержимое уничтожается первым же up --force-recreate.
У песочницы кода отдельная сеть и ни одного секрета. js-executor подключён к сети,
где нет ни базы, ни Redis, ни Qdrant, запущен с read-only корнем, без capabilities, с
ограничением по памяти и числу процессов, и env_file ему намеренно не выдаётся. Смысл в
том, что побег из песочницы попадает в контейнер, в котором нечего красть. Подробнее про
сами ноды — в «Свой код».
Что обязательно настроить до первого запуска
Порядок такой: скопировать infra/prod/.env.example в infra/prod/.env и заполнить.
С ENVIRONMENT=production приложение проверяет конфигурацию на старте и отказывается
запускаться, если опасное значение оставлено дефолтным, — это сделано специально, чтобы
установка не поехала с ключом из примера.
| Переменная | Что это | Если не задать |
|---|---|---|
| SECRET_KEY | мастер-секрет: из него выводятся подпись JWT и подписи ссылок на файлы | старт прерывается |
| CONNECTIONS_FERNET_KEY | ключ шифрования секретов подключений в базе | старт прерывается |
| CORS_ORIGINS | с каких origin браузеру разрешено звать API — строго JSON-массив | «*» в проде запрещён; не-JSON рвёт старт |
| POSTGRES_PASSWORD + DATABASE_URL | пароль базы; строка подключения должна совпадать с ним | приложение не подключится |
| PUBLIC_BASE_URL | внешний адрес приложения — по нему регистрируются вебхуки | публикация в Telegram невозможна |
| OPENROUTER_API_KEY | ключ провайдера моделей платформы | каталог виден, но любой вызов модели падает |
Ещё два адреса задаются на сборке образа, а не в рантайме: PUBLIC_SITE_URL (куда
приложение ведёт ссылками на документацию) и PUBLIC_APP_URL. Vite и Astro подставляют их
в бандл в момент сборки, поэтому смена требует пересборки образа, а не перезапуска. Если
PUBLIC_SITE_URL не задан — приложение просто не рисует ссылок на документацию, и это
правильное поведение для установки, которая сайт не публикует.
TLS входной прокси не терминирует: в поставляемой конфигурации edge слушает обычный
HTTP и маршрутизирует по домену (/api/… → api, корень основного домена → сайт, домен
приложения → SPA). Сертификаты — на внешнем балансировщике или расширением этой
конфигурации; это задача администратора инстанса.
Первый администратор
Глобальная роль admin — это то, что открывает пункт меню Админка (админку) и снимает лимиты
с аккаунта. Первый такой пользователь провижнится через BOOTSTRAP_ADMIN_EMAILS, и здесь
важен порядок:
- Поднять стек и зарегистрироваться обычным способом через интерфейс.
- Вписать этот email в
BOOTSTRAP_ADMIN_EMAILSв.env(JSON-массив). - Перезапустить
api: список применяется на старте и повышает роль уже существующих пользователей. Несуществующий email не создаёт аккаунт — он просто ни на что не влияет.
Тот же результат даёт скрипт scripts/grant_admin.py, запущенный в контейнере api. Роль
читается из базы на каждый запрос, поэтому повышение и понижение действуют сразу, без
перевхода. Разбор ролей — в «Доступе команды».
Каталоги моделей — файлы, а не код
Список моделей, которые видят ноды, приезжает не из кода, а из YAML-каталогов в
backend/config/: llm/, embeddings/, rerank/, media/. Один файл — один провайдер:
его имя, класс-адаптер, базовый адрес, ключ (через подстановку переменной окружения) и
список моделей с ценами в кредитах. Добавить модель — правка файла и перезапуск, а не
изменение кода. Как устроены поля — в «Каталоге моделей».
Отсюда и главная возможность своей установки: указать в params.base_url собственный
OpenAI-совместимый эндпоинт (LM Studio, vLLM, TEI) и получить модели, которые никуда наружу
не ходят. Такой каталог уже лежит в комплекте как пример — config/llm/local.yaml с
переменными LOCAL_BASE_URL / LOCAL_API_KEY.
Тарифы, лимиты и деньги на своей установке
Планы живут строками в базе, а не константами: администратор правит числа во вкладке Тарифы админки и точечно переопределяет лимиты конкретному пользователю. Отсутствующий в плане ключ означает «поведение по умолчанию» — значение из конфигурации приложения. Что именно ограничивается, перечислено в «Лимитах тарифа», а кредиты и их списание — в «Кредитах».
Учёт кредитов работает и на своей установке: каждый запуск и каждый вызов модели пишет
строку в журнал использования. Это полезно даже когда деньги никто не собирает — видно, во
что обходится конкретный воркфлоу. Полностью выключить тарифные проверки можно
переключателем PLAN_ENFORCEMENT_ENABLED=false; тогда квоты, потолки параллельности и
гейты по кредитам перестают применяться ко всем.
Самостоятельная покупка тарифа пользователем в поставке выключена
(BILLING_SELF_SERVICE_ENABLED=false), и это не оплошность: провайдер по умолчанию —
заглушка, чей эндпоинт подтверждения выдаёт кредиты и платный план без реального платежа.
Проверка старта отказывается поднимать production с включённой самообслуживаемой оплатой на
заглушке. На своей установке роли раздаёт администратор, а не касса.
Чем своя установка отличается от облачной
| Вопрос | Своя установка |
|---|---|
| Чьи ключи провайдеров | ваши, в .env инстанса; можно вообще не выпускать наружу |
| Какие модели доступны | какие перечислены в ваших YAML — включая локальные |
| Кто задаёт лимиты | администратор инстанса в админке; проверки можно и выключить |
| Где лежат данные | ваши тома: postgres, qdrant, файлы приложения |
| Ноды с кодом | включаются явно и только вместе с контейнером-песочницей |
| Обновления и бэкапы | ваша ответственность — в поставке демона бэкапов нет |
Что платформа не делает за администратора: не выпускает сертификаты, не снимает бэкапы, не поднимает мониторинг (сбор трасс и метрик через OTLP есть, но коллектор в этот compose не входит — его адрес задаётся отдельно) и не обновляется сама. Ретеншен логов запусков при этом автоматический: чистку делает сам воркер по сроку из тарифа владельца, отдельного сервиса для этого нет.
Сколько это просит ресурсов
Суммарные лимиты памяти всех девяти контейнеров в поставляемом файле — 8320 МиБ
(воспроизвести: сложить значения memory: в infra/prod/docker-compose.yml). Это лимиты, а
не потребление, но планировать машину меньше 8 ГБ смысла нет; в плане мощностей
(infra/CAPACITY_PLAN.md) для старта рекомендованы 8 vCPU / 16 ГБ на одну машину.
Дальше упирается в две вещи: число одновременных запусков (MAX_PARALLEL_RUNS на процесс
воркера) и пул соединений к Postgres — они связаны, и поднимать первое без второго
бессмысленно. Соответствующие переменные и правило их согласования описаны комментариями в
backend/.env.example.
Данные и безопасность
Что где хранится, как шифруются секреты подключений, что видно наружу.
Каталог моделей
Как устроен YAML провайдера и как добавить свою модель.
Админка
Тарифы, переопределения лимитов конкретному пользователю, журнал аудита.
Свой код
Ноды с JavaScript и контейнер, в котором они исполняются.