Перейти к содержимому
OmniRoute source

🐳 Docker Guide — OmniRoute (Русский)

Самостоятельное размещение одной командой? См. руководство по самостоятельному размещению — docker compose -f docker-compose.selfhost.yml up -d (опубликованный образ + Redis, доступ только через loopback-интерфейс, без выбора профиля). Приведённый ниже быстрый запуск — это вариант с одним контейнером для пользователей, у которых Redis уже запущен отдельно.

Окно терминала
docker run -d \
--name omniroute \
--restart unless-stopped \
--stop-timeout 40 \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
Окно терминала
# Сначала скопируйте и отредактируйте .env
cp .env.example .env
docker run -d \
--name omniroute \
--restart unless-stopped \
--stop-timeout 40 \
--env-file .env \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
Окно терминала
# Базовый профиль (без инструментов CLI)
docker compose --profile base up -d
# Профиль CLI (встроенные Claude Code, Codex, OpenClaw)
docker compose --profile cli up -d
# Профиль хоста (в первую очередь для Linux; монтирует бинарные файлы CLI хоста в режиме только для чтения)
docker compose --profile host up -d
# Веб-профиль (Chromium/Playwright для провайдеров веб-сессий)
docker compose --profile web up -d
# Совместное использование CLI и вспомогательного контейнера CLIProxyAPI
docker compose --profile cli --profile cliproxyapi up -d

OmniRoute предоставляет профили Compose для основных вариантов развёртывания. Выберите профиль, соответствующий вашей среде.

Профиль Сервис Когда использовать Команда
base (по умолчанию) omniroute-base Сервер без графического интерфейса / минимальная среда выполнения, CLI провайдеров не включены docker compose --profile base up -d
cli omniroute-cli Агентные рабочие процессы, вызывающие omniroute providers/setup/doctor и включённые CLI (Codex, Claude Code, Droid, OpenClaw) docker compose --profile cli up -d
host omniroute-host Хосты Linux, которым нужен доступ к CLI хоста, аналогичный network_mode, путём монтирования ~/.local/bin, ~/.codex, ~/.claude и т. д. только для чтения docker compose --profile host up -d
cliproxyapi cliproxyapi Запуск вспомогательного контейнера CLIProxyAPI на порту 8317 для проксирования вышестоящих CLI docker compose --profile cliproxyapi up -d
web omniroute-web Провайдеры веб-сеансов, которым нужен браузер: gemini-web, claude-web, claude-turnstile (собирает runner-web, Chromium включён) docker compose --profile web up -d

Можно объединить несколько профилей: docker compose --profile cli --profile cliproxyapi up -d.

Настройка CLI-инструментов хоста при запуске OmniRoute в Docker

Заголовок раздела «Настройка CLI-инструментов хоста при запуске OmniRoute в Docker»

omniroute setup-codex, setup-claude, config set <tool> и кнопка Сохранить конфигурацию на панели управления записывают файлы наподобие ~/.codex/*.config.toml. Эти пути имеют смысл только на машине, где фактически работает CLI. Если запустить эти команды внутри контейнера, запись попадёт в собственный домашний каталог контейнера (/home/node — образ запускается с USER node), откуда ни один CLI на хосте никогда её не прочитает и где она будет удалена сразу после пересоздания контейнера.

OmniRoute обнаруживает такую ситуацию и вместо сообщения об успехе, результатом которого невозможно воспользоваться, отклоняет запись и выводит инструкции: CLI завершается с кодом 2, а API отвечает 422 с containerEphemeralTarget: true.

Рекомендуемый вариант: запускать CLI на хосте, а OmniRoute — в Docker

Заголовок раздела «Рекомендуемый вариант: запускать CLI на хосте, а OmniRoute — в Docker»

Контейнер предоставляет API, а CLI настраивает ваши инструменты на хосте.

Окно терминала
docker compose --profile base up -d
npm install -g omniroute
omniroute connect http://localhost:20128 # подключить CLI к контейнеру
omniroute setup-codex # записывает реальную конфигурацию ~/.codex на хосте

Это правильный вариант, если Codex, Claude Code, Cursor или аналогичные инструменты работают на вашем ноутбуке, что соответствует обычной конфигурации.

Альтернатива: bind-монтирование каталогов конфигурации хоста (профиль host)

Заголовок раздела «Альтернатива: bind-монтирование каталогов конфигурации хоста (профиль host)»

Если вы хотите, чтобы контейнер самостоятельно записывал конфигурацию на хосте, смонтируйте каталоги внутрь контейнера и укажите корень точки монтирования в CLI_CONFIG_HOME. В профиле host это уже настроено:

environment:
- CLI_CONFIG_HOME=/host-home
- CLI_ALLOW_CONFIG_WRITES=true
volumes:
- ~/.codex:/host-home/.codex:rw
- ~/.claude:/host-home/.claude:rw

Именно bind-монтирование делает путь доверенным: OmniRoute читает /proc/self/mountinfo и разрешает запись в смонтированные пути (а также в каталоги, дочерние элементы которых являются точками монтирования, что в точности соответствует приведённой выше структуре /host-home), продолжая при этом отклонять запись в несмонтированные пути.

Обходной вариант: настройка CLI-инструментов самого контейнера (используйте с осторожностью)

Заголовок раздела «Обходной вариант: настройка CLI-инструментов самого контейнера (используйте с осторожностью)»

Если CLI действительно находятся внутри контейнера (профиль cli), запись является преднамеренной. Передайте --allow-container-write любой команде setup-* или задайте OMNIROUTE_ALLOW_CONTAINER_CONFIG_WRITE=true для сервера. Запись будет выполнена с предупреждением о том, что она не сохранится после пересоздания контейнера.

Предупреждение о безопасности — профиль cli + монтирование docker.sock. Профиль cli bind-монтирует /var/run/docker.sock, чтобы работающий внутри контейнера механизм автоматического обновления мог пересоздать стек через демон хоста (src/lib/system/autoUpdate.ts проверяет наличие этого сокета и пропускает путь Docker, если он отсутствует). Этот сокет является границей доверия с root-доступом к хосту: всё, что может обращаться к нему, управляет демоном Docker на хосте от имени root — оно может создавать, просматривать, останавливать и удалять любые контейнеры на хосте. Следствия:

  1. Никогда не открывайте порт профиля cli для сети. Публикуйте его на 127.0.0.1 (ports: "127.0.0.1:${DASHBOARD_PORT:-20128}:...") — доступность профиля cli из локальной сети превращает любую уязвимость RCE на уровне панели управления в полный захват хоста.
  2. Не bind-монтируйте дополнительные каталоги хоста в профиль cli. Сокет Docker в сочетании с любым дополнительным монтированием предоставляет контейнеру полный доступ на чтение и запись к вашей файловой системе и конфигурации хоста. Если инструменту нужен доступ к проекту, запускайте его локально с помощью исполняемого файла CLI — не монтируйте проект в контейнер cli.

Если автоматическое обновление внутри контейнера не требуется, не включайте профиль cli (COMPOSE_PROFILES=core,redis или более короткий вариант). Остальные профили не монтируют сокет Docker.

Связанная модель угроз для MITM описана в docs/security/MITM-TPROXY-DECRYPT.md (git; не включается в /docs), а цепочка происхождения исполняемых файлов codex/claude-code/droid/openclaw — в docs/security/SUPPLY_CHAIN.md.

OmniRoute использует Redis для работы распределённого ограничителя частоты запросов и общего кеша. Сервис redis всегда определён в docker-compose.yml (он не привязан к профилю) и запускается вместе с любым другим профилем.

Параметр Значение
Образ redis:7-alpine
Имя контейнера omniroute-redis
Внутренний порт 6379
Порт хоста (переопределяемый) REDIS_PORT (по умолчанию 6379)
Адрес хоста (переопределяемый) REDIS_BIND_HOST (по умолчанию 127.0.0.1)
Том omniroute-redis-data → /data
Проверка работоспособности redis-cli ping (интервал 10 с)

Связанные переменные окружения:

  • REDIS_URL — строка подключения, передаваемая приложению (по умолчанию redis://redis:6379).
  • REDIS_PORT — порт на стороне хоста, сопоставленный с портом контейнера Redis.
  • REDIS_BIND_HOST — интерфейс хоста, на котором публикуется порт. По умолчанию 127.0.0.1.

Почему по умолчанию используется loopback-интерфейс: сайдкар запускается без requirepass, а контейнеры приложения обращаются к нему через сеть compose (redis:6379) — опубликованный порт нужен только для инструментов на стороне хоста (redis-cli, локальный запуск npm run dev). Публикация на 0.0.0.0 предоставила бы каждому хосту в вашей локальной сети доступ к Redis без аутентификации. Если вы задаёте REDIS_BIND_HOST=0.0.0.0, также добавьте --requirepass в command: сервиса.

Отключать Redis не рекомендуется (ограничитель частоты запросов переключится на менее надёжный резервный вариант в памяти). Если это необходимо, удалите или закомментируйте блок сервиса redis: в docker-compose.yml либо уменьшите количество его экземпляров до нуля:

Окно терминала
docker compose up -d --scale redis=0

Для изолированного production-снимка, работающего параллельно со средой разработки, используйте docker-compose.prod.yml.

Параметр Значение
Файл docker-compose.prod.yml
Порт панели управления по умолчанию PROD_DASHBOARD_PORT=20130 (сопоставлен с внутренним ${DASHBOARD_PORT:-20128})
Порт API по умолчанию PROD_API_PORT=20131
Образ omniroute:prod (собирается из цели runner-cli)
Контейнер Redis omniroute-redis-prod (redis:8.6.2, выделенный том redis-prod-data)
Том данных omniroute-prod-data (именованный, сохраняется между пересборками)
Проверки работоспособности node healthcheck.mjs + redis-cli ping, при этом depends_on ожидает готовности Redis

Использование:

Окно терминала
# Собрать и запустить production-стек
docker compose -f docker-compose.prod.yml up -d --build
# Просматривать поток журналов
docker compose -f docker-compose.prod.yml logs -f
# Остановить и удалить стек (сохранив тома)
docker compose -f docker-compose.prod.yml down

Production-стек работает параллельно с dev-конфигурацией compose (используются разные имена контейнеров, порты и тома), поэтому вы можете продолжать локальную разработку, пока production-среда остаётся запущенной.

Репозиторий содержит многоэтапный Dockerfile (Dockerfile). Доступны четыре этапа; выберите подходящий target для своего сценария использования.

Этап Базовый образ Назначение
builder node:26-trixie-slim Устанавливает зависимости (npm ci --legacy-peer-deps) и запускает npm run build (по умолчанию используется Turbopack — см. раздел «Ресурсы во время сборки» ниже)
runner-base node:26-trixie-slim Среда выполнения для рабочей среды с автономным выводом Next.js. CLI провайдеров не включены.
runner-cli runner-base Добавляет git, docker.io, docker-compose и глобальные CLI: @openai/codex, @anthropic-ai/claude-code, droid, openclaw. Выберите этот этап для агентных рабочих процессов.
runner-web runner-base Добавляет Playwright и браузер Chromium (--with-deps) для провайдеров веб-сессий: gemini-web, claude-web, claude-turnstile. Выберите этот этап при использовании этих провайдеров — обычный образ без него завершится ошибкой при выполнении запроса (см. примечание о -web в разделе «Каналы выпуска»).

Сборка конкретного целевого этапа вручную:

Окно терминала
docker build --target runner-base -t omniroute:base .
docker build --target runner-cli -t omniroute:cli .
docker build --target runner-web -t omniroute:web .

Три аргумента сборки определяют ресурсоёмкость этапа builder. Они применяются только во время сборки — OMNIROUTE_MEMORY_MB (см. ниже) является отдельным параметром среды выполнения.

Аргумент сборки По умолчанию Эффект
OMNIROUTE_USE_TURBOPACK 1 Значение 0 включает сборку с помощью webpack. Меньше пиковое потребление памяти, но ниже скорость.
OMNIROUTE_BUILD_MEMORY_MB 6144 Предельный размер кучи V8 (--max-old-space-size) для запускаемого процесса next build.
OMNIROUTE_BUILD_WORKERS 2 Передаёт значение в CIRCLE_NODE_TOTAL; Next вычисляет workers = N - 1 для сбора данных страниц.

OMNIROUTE_BUILD_WORKERS следует увеличивать на мощной системе сборки, и именно его нужно проверить в первую очередь, если сборка в условиях ограниченных ресурсов завершается после ✓ Compiled successfully. Каждый процесс обработки данных страниц выполняется в отдельном процессе, как и родительский процесс next build; воспроизведение на рабочем VPS (проблема #7518) показало, что пиковый RSS каждого процесса составляет ~4.5 GB независимо от флага кучи NODE_OPTIONS (Turbopack выполняет компиляцию в нативной памяти/Rust за пределами кучи V8). Значение по умолчанию 2 (→ 1 рабочий процесс, всего 2 процесса) рассчитано на размещённые в GitHub исполнители с 16 GB памяти и 4 vCPU, используемые конвейером публикации. При значении 8 (→ 7 рабочих процессов) у такого исполнителя закончилась память, и buildkit завершил этап с ошибкой ResourceExhausted: ... cannot allocate memory; значение 3 (→ 2 рабочих процесса) также не уложилось в лимит после прямого измерения RSS каждого процесса вместо расчётной оценки. tests/unit/docker-build-memory-budget.test.ts выполняет расчёты на основе измеренного значения и завершается ошибкой, если какой-либо из параметров превышает возможности исполнителя.

Turbopack выполняет компиляцию в нативной памяти Rust, расположенной за пределами кучи V8, поэтому OMNIROUTE_BUILD_MEMORY_MB её не ограничивает. На хосте с ограничением памяти сборка затем принудительно завершается OOM-механизмом посредством SIGKILL вообще без текста ошибки — она просто останавливается посреди Creating an optimized production build, что больше похоже на зависание, чем на нехватку памяти. Если ресурсы хоста сборки ограничены, смените сборщик:

Окно терминала
docker build --target runner-base \
--build-arg OMNIROUTE_USE_TURBOPACK=0 \
-t omniroute:base .

Параметр webpackBuildWorker включён, поэтому next build запускает родительский и рабочий процесс, каждый из которых отдельно учитывает OMNIROUTE_BUILD_MEMORY_MB. Устанавливайте лимит контейнера примерно вдвое выше этого значения, а не равным ему.

Результаты измерений для этого дерева (--target runner-base, OMNIROUTE_BUILD_MEMORY_MB=6144):

Сборщик Лимит контейнера Результат
Turbopack 8 GiB / 16 GiB OOM-завершение при обоих лимитах, без сообщений
webpack 8 GiB рабочий процесс сборки завершён сигналом SIGKILL
webpack 12 GiB успешно, пиковое потребление составило 11.1 GiB

Значения по умолчанию, экспортируемые runner-base: PORT=20128, HOSTNAME=0.0.0.0, OMNIROUTE_MEMORY_MB=1024, NODE_OPTIONS=--max-old-space-size=1024, DATA_DIR=/app/data, OMNIROUTE_MIGRATIONS_DIR=/app/migrations.

Поведение памяти в Docker:

  • Образ задаёт OMNIROUTE_MEMORY_MB=1024 и на его основе формирует NODE_OPTIONS=--max-old-space-size=1024.
  • Фактический серверный процесс запускается автономным загрузчиком, который считывает OMNIROUTE_MEMORY_MB и добавляет --max-old-space-size=<OMNIROUTE_MEMORY_MB>.
  • Node использует последнее повторяющееся значение --max-old-space-size, поэтому параметр OMNIROUTE_MEMORY_MB определяет фактический лимит кучи в Docker.
  • Поскольку образ всегда задаёт этот параметр, собственное резервное значение загрузчика, рассчитанное на основе объёма RAM, в Docker никогда не применяется. Явно увеличьте его с учётом рабочей нагрузки (см. таблицу ниже). 2048 всё ещё недостаточно для запросов агентов программирования к /v1/responses.

Оперативная память для агентов программирования

Заголовок раздела «Оперативная память для агентов программирования»

Значение Docker по умолчанию, равное 1 ГиБ, — это минимальный объём для панели управления и простых чатов, а не конфигурация для промышленной эксплуатации. Длинные тела запросов POST /v1/responses (сотни сообщений, десятки инструментов) во время сжатия удерживают в памяти несколько графов. Два параллельных запроса объёмом около 3 МиБ / 750 тыс. токенов приводили к аварийному завершению V8 при 12 ГиБ old-space (FATAL ERROR: Reached heap limit), а также к OOM в cgroup с лимитом 16 ГиБ. См. #7849.

Устанавливайте лимит cgroup --memory выше размера кучи — нативные буферы, SQLite и промежуточные данные сжатия размещаются за пределами V8.

Рабочая нагрузка OMNIROUTE_MEMORY_MB Контейнер / cgroup Примечания
Панель управления, один простой чат 1024 (по умолчанию в образе) ≥2 ГиБ
Один агент программирования (Claude/Codex/Grok) 8192 ≥10 ГиБ Типичный одиночный сеанс /v1/responses
Два параллельных длинных запроса /v1/responses 10240–12288 ≥12–16 ГиБ Зафиксировано аварийное завершение V8 при куче около 12 ГиБ
Три и более параллельных длинных контекста не используйте в одном процессе последовательная обработка / больше RAM По умолчанию допускается 1 ресурсоёмкий запрос в обработке; увеличение этого значения без дополнительной RAM снова приводит к аварийному завершению

При запуске omniroute serve на физическом сервере рассчитывается около 35% от объёма RAM (с ограничением диапазоном [512, 4096]), если OMNIROUTE_MEMORY_MB не задана. Docker всегда задаёт значение 1024, поэтому в официальном образе этот расчёт никогда не выполняется.

Окно терминала
docker run -d --name omniroute --restart unless-stopped --stop-timeout 40 \
-e OMNIROUTE_MEMORY_MB=8192 --memory=10g \
-p 127.0.0.1:20128:20128 -v omniroute-data:/app/data diegosouzapw/omniroute:latest

Помимо значений по умолчанию, описанных в ENVIRONMENT.md, при запуске в Docker наиболее важны следующие переменные:

Переменная Назначение Значение по умолчанию
OMNIROUTE_WS_BRIDGE_SECRET Общий секрет для моста WebSocket. Обязателен в рабочей среде — задайте надежную случайную строку. не задано (необходимо указать)
REDIS_URL Строка подключения к серверной части ограничителя частоты запросов / кеша redis://redis:6379
REDIS_PORT Порт хоста для входящего в комплект контейнера Redis 6379
REDIS_BIND_HOST Интерфейс хоста, на котором публикуется порт входящего в комплект Redis (кольцевой интерфейс, если не добавлена AUTH) 127.0.0.1
AUTO_UPDATE_HOST_REPO_DIR Путь на хосте, подключаемый к профилю cli по адресу /workspace/omniroute для рабочих процессов самообновления . (текущий каталог)
OMNIROUTE_MEMORY_MB Максимальный размер кучи Node во время выполнения для автономного сервера Docker; переопределяет указанное выше значение по умолчанию из образа. Для агентов программирования: 8192+ (см. оперативная память среды выполнения). 1024
DASHBOARD_PORT / API_PORT Переопределяют опубликованные порты панели управления (20128) и API (20129) 20128 / 20129
APP_BIND_HOST Интерфейс хоста, на котором docker-compose публикует порты панели управления/API/live-WS. При REQUIRE_API_KEY=false (значение по умолчанию) 0.0.0.0 открывает анонимный прокси /v1 для локальной сети — расширяйте доступ только при REQUIRE_API_KEY=true или при наличии обратного прокси перед приложением. 127.0.0.1
CLIPROXY_BIND_HOST Интерфейс хоста, на котором docker-compose публикует дополнительный контейнер cliproxyapi; его том данных содержит учетные данные провайдера. 127.0.0.1
OMNIROUTE_PLUGINS_DIR Каталог, который сканер плагинов среды выполнения читает и использует для установки. Задайте его, если плагины подключаются через bind mount: значение по умолчанию зависит от HOME, который не обязательно экспортируется образом. ~/.omniroute/plugins
OMNIROUTE_BASE_PATH Подпуть URL, если приложение публикуется за обратным прокси (например, /omniroute) (пусто = корень)
NEXT_PUBLIC_BASE_URL Публичный источник браузера, включающий подпуть (например, https://host/omniroute) не задано
PROD_DASHBOARD_PORT Порт панели управления на стороне хоста для docker-compose.prod.yml 20130
CLIPROXYAPI_PORT Порт на стороне хоста для дополнительного контейнера cliproxyapi 8317

Значение basePath Next.js компилируется в автономный пакет. OmniRoute записывает встроенное значение в сигнальный файл в корне приложения (записывается во время npm run build; считывается scripts/docker/ensure-docker-base-path.mjs) и сравнивает его с OMNIROUTE_BASE_PATH при запуске контейнера. Если значения различаются, а образ был собран для корня домена, точка входа изменяет автономные манифесты, встроенные литералы basePath/assetPrefix (Next 16 формирует URL-адреса ресурсов SSR только из assetPrefix — патчер добавляет в него подпуть), встроенные URL-адреса ресурсов /_next/static (манифесты клиентских ссылок, импорты медиафайлов, предварительно отрендеренные страницы ошибок) и клиентскую прослойку process.env перед запуском node dev/run-standalone.mjs.

Задайте обе переменные в .env, затем пересоберите образ, чтобы настройки образа и среды выполнения совпадали:

.env
OMNIROUTE_BASE_PATH=/omniroute
NEXT_PUBLIC_BASE_URL=https://myhostname.example.com/omniroute
Окно терминала
docker compose --profile base up -d --build

docker-compose.yml передаёт OMNIROUTE_BASE_PATH как аргумент сборки Docker и как переменную среды выполнения.

Предварительно собранный корневой образ + подпуть среды выполнения

Заголовок раздела «Предварительно собранный корневой образ + подпуть среды выполнения»

Опубликованные образы diegosouzapw/omniroute:* собраны для корня домена. Тем не менее можно задать OMNIROUTE_BASE_PATH во время выполнения; контейнер однократно исправит пакет при запуске. Укажите также соответствующий публичный источник:

services:
omniroute:
image: diegosouzapw/omniroute:latest
environment:
OMNIROUTE_BASE_PATH: /omniroute
NEXT_PUBLIC_BASE_URL: https://myhostname.example.com/omniroute

Настройте обратный прокси так, чтобы он перенаправлял полный внешний путь (не удаляйте префикс). Traefik должен направлять PathPrefix(/omniroute) в контейнер без StripPrefix, чтобы Next.js получал /omniroute/... и отдавал ресурсы из /omniroute/_next/....

Проверка работоспособности Docker обращается к облегчённой конечной точке жизненного цикла /healthz с префиксом активного значения OMNIROUTE_BASE_PATH. /api/monitoring/health остаётся доступной для диагностики вручную или с помощью панели мониторинга; чтобы снова направить HEALTHCHECK контейнера на неё (например, для углублённой проверки работоспособности), задайте OMNIROUTE_HEALTHCHECK_PATH=/api/monitoring/health. Этот путь выполняет углублённую проверку (БД + сводка мониторинга) — она подходит для нечастого HEALTHCHECK Docker, если вы решите снова её включить, но не для интервалов livenessProbe Kubernetes.

Для оркестраторов (Kubernetes, Nomad и т. д.):

Проверка Предпочтительно Не рекомендуется
Жизнеспособность HTTP GET /livez или TCP на основном порту (PORT, по умолчанию 20128) /api/monitoring/health для проверки жизнеспособности
Готовность HTTP GET /healthz Короткие тайм-ауты, при которых занятый цикл событий считается отказавшим
Углублённая / blackbox /api/monitoring/health —

/healthz сообщает о состоянии жизненного цикла процесса (ok / starting / stopping). /livez проверяет только работу процесса (возвращает 200, если обработчик может выполняться; ожидания готовности нет). Обе проверки по-прежнему выполняются в том же цикле событий Node, что и обработка запросов, поэтому ресурсоёмкая обработка каталога или сжатие могут задерживать их — занятость ≠ отказ. Если HTTP-проверки завершаются по тайм-ауту, для проверки жизнеспособности предпочтительно использовать TCP. Полное руководство по проверкам: Руководство по мониторингу — рекомендации по проверкам Kubernetes.

OmniRoute можно безопасно опубликовать с помощью автоматической настройки SSL в Caddy. Убедитесь, что DNS-запись A вашего домена указывает на IP-адрес вашего сервера.

services:
omniroute:
image: diegosouzapw/omniroute:latest
container_name: omniroute
restart: unless-stopped
volumes:
- omniroute-data:/app/data
environment:
- PORT=20128
# Публичный адрес для обратных вызовов OAuth, ссылок панели управления и создаваемых публичных URL.
- NEXT_PUBLIC_BASE_URL=https://your-domain.com
# Внутренний межсерверный URL для запланированных заданий и запросов к самому сервису.
- BASE_URL=http://omniroute:20128
- AUTH_COOKIE_SECURE=true
caddy:
image: caddy:latest
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
command: caddy reverse-proxy --from https://your-domain.com --to http://omniroute:20128
volumes:
omniroute-data:

Caddy устанавливает стандартные заголовки переадресации для вышестоящего контейнера. OmniRoute использует NEXT_PUBLIC_BASE_URL в качестве канонического публичного источника для обратных вызовов OAuth и создаваемых публичных ссылок; аутентифицированные операции записи из панели управления используют запросы того же источника и привязанную к сеансу защиту CSRF. Включайте OMNIROUTE_TRUST_PROXY только в расширенных конфигурациях, где вы намеренно хотите, чтобы OmniRoute определял публичный источник по доверенным перенаправленным заголовкам вместо явной конфигурации.

Поддержка панели управления для развёртываний Docker включает включаемый одним щелчком быстрый туннель Cloudflare в разделе Dashboard → Endpoints. При первом включении cloudflared загружается только при необходимости, запускается временный туннель к вашему текущему эндпоинту /v1, а созданный URL https://*.trycloudflare.com/v1 отображается непосредственно под вашим обычным публичным URL.

Панели туннелей эндпоинта (Cloudflare, Tailscale, ngrok) можно показывать или скрывать в разделе Settings → Appearance, не изменяя состояние активного туннеля.

  • URL быстрых туннелей являются временными и меняются после каждого перезапуска.
  • Быстрые туннели не восстанавливаются автоматически после перезапуска OmniRoute или контейнера. При необходимости повторно включите их через панель управления.
  • Управляемая установка в настоящее время поддерживает Linux, macOS и Windows на x64 / arm64.
  • Управляемые быстрые туннели по умолчанию используют транспорт HTTP/2, чтобы избежать многочисленных предупреждений о размере UDP-буфера QUIC в контейнерных средах с ограниченными ресурсами. Установите CLOUDFLARED_PROTOCOL=quic или auto, если хотите использовать другой транспорт.
  • Образы Docker включают системные корневые сертификаты ЦС и передают их управляемому cloudflared, что предотвращает ошибки доверия TLS при инициализации туннеля внутри контейнера.
  • Установите CLOUDFLARED_BIN=/absolute/path/to/cloudflared, если хотите, чтобы OmniRoute использовал существующий исполняемый файл вместо его загрузки.
Образ Тег Размер Описание
diegosouzapw/omniroute latest ~250MB Самая новая опубликованная стабильная версия SemVer (не git main)
diegosouzapw/omniroute 3.8.0 ~250MB Закрепляйте теги этого типа для GitOps

Мультиплатформенный манифест: нативные linux/amd64 + linux/arm64 (Apple Silicon, AWS Graviton, Raspberry Pi). Docker автоматически выбирает подходящую архитектуру; передайте --platform linux/amd64, если необходимо принудительно использовать эмуляцию AMD64 на хостах ARM.

OmniRoute публикует отдельные Docker-каналы для стабильных выпусков, тестирования активной ветки выпуска и сборок для разработки.

Канал Источник Изменяемость Рекомендуемое применение
:&lt;version&gt; / :&lt;version&gt;-web Подписанный/версионированный выпуск Неизменяемый Рабочие развертывания с закреплением точного выпуска
:latest / :latest-web Самая новая опубликованная стабильная версия SemVer Изменяемый указатель на стабильную версию Следует за стабильными выпусками после задания публикации SemVer — не отслеживает main или невыпущенные коммиты release/v*
:next / :next-web Текущая ветка release/v* по умолчанию Изменяемый указатель на предварительный выпуск Тестирование исправлений, которые уже попали в активную ветку выпуска, но еще не вошли в стабильный выпуск
:main / :main-web Ветка main Изменяемый указатель на версию для разработки Только для разработки и интеграционного тестирования

Для каждого указанного выше канала также существует тег -web (:latest-web, :&lt;version&gt;-web, :next-web, :main-web), собранный из этапа runner-web — это тот же образ, но с Playwright и браузером Chromium. Обычный образ поставляется без Chromium; он необходим для gemini-web, claude-web и claude-turnstile.

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

[500]: Failed to load external module playwright: Error: Cannot find module
'/app/node_modules/playwright/node_modules/playwright-core/browsers.json'

Если вы используете эти провайдеры, загрузите тег -web того канала, на котором уже находитесь, — больше ничего менять не нужно. При установке через npm/CLI (без образа Docker) недостающим компонентом является исполняемый файл браузера: выполните npx playwright install chromium на хосте.

Использование канала предварительных выпусков

Заголовок раздела «Использование канала предварительных выпусков»

Канал next пересобирается при каждой отправке изменений в текущую ветку release/v* по умолчанию и публикуется как для AMD64, так и для ARM64. Более старые ветки сопровождения не могут его перезаписать. Этот канал предоставляет доступный для загрузки образ с исправлениями, которые были объединены с активной веткой выпуска до создания следующего стабильного тега.

Окно терминала
docker pull diegosouzapw/omniroute:next
docker pull diegosouzapw/omniroute:next-web

Для Docker Compose переопределите тег образа, используемый выбранным профилем, затем загрузите образ и пересоздайте сервис:

services:
omniroute:
image: diegosouzapw/omniroute:next
Окно терминала
docker compose pull
docker compose up -d

next — это плавающий канал предварительных выпусков. Он может измениться при любой отправке изменений в активную ветку выпуска и не поддерживается для использования в рабочей среде. При оценке конкретной сборки закрепите дайджест образа:

Окно терминала
docker pull diegosouzapw/omniroute:next
docker image inspect diegosouzapw/omniroute:next --format '{{index .RepoDigests 0}}'

Перед тестированием создайте резервную копию тома данных OmniRoute или каталога данных, подключенного через bind mount. Для отката восстановите ранее использовавшуюся стабильную версию или дайджест и пересоздайте контейнер:

Окно терминала
docker pull diegosouzapw/omniroute:&lt;stable-version&gt;
docker compose up -d

Сборка из ветки выпуска никогда не может изменить latest; стабильный указатель может быть обновлен только подходящей стабильной семантической версией. Для образов next сохраняются проверка образа выпуска и блокирующий шлюз для уязвимостей уровня CRITICAL.

latest не гарантирует актуальность относительно git. Объединенные исправления в main или активной ветке release/v* не попадают в :latest, пока не будет опубликован образ стабильной версии SemVer и задание публикации не обновит :latest (с тем же дайджестом, что и у этой версии SemVer). Если latest кажется замороженным, хотя исправление уже отображается на GitHub, загрузите :next, чтобы протестировать ветку выпуска, или дождитесь тега SemVer.

Что вам нужно Что использовать
GitOps / рабочая среда без самопроизвольного обновления Закрепите :X.Y.Z (или дайджест образа)
Следовать за опубликованными стабильными версиями и пересоздавать контейнер при каждом выпуске :latest
Тестировать невыпущенные коммиты release/v* :next (не для рабочей среды)
Тестировать main :main (не для рабочей среды)

Доступность: SQLite по умолчанию работает с одной репликой

Заголовок раздела «Доступность: SQLite по умолчанию работает с одной репликой»

Стандартное развёртывание OmniRoute в Docker / Kubernetes — это один процесс Node + один процесс записи SQLite. Высокая доступность в такой топологии не поддерживается.

Ограничение Последствие
Один процесс записи Не запускайте несколько реплик с одним и тем же файлом SQLite. Это приведёт к повреждению БД.
Пересоздание / перезапуск / завершение через HEALTHCHECK Полная недоступность активных SSE-соединений, сеансов панели управления и состояния в памяти. Все подключённые клиенты отключаются. Новые запросы в период отсутствия конечных точек получают от обратного прокси 502 Bad Gateway: Unknown error, а не JSON от OmniRoute — клиенты не могут отличить это от сбоя провайдера (#11015).
Один цикл событий с /healthz Загруженная операция с каталогом или цикл сжатия могут задержать проверки; короткий тайм-аут после этого перезапустит единственную реплику.

Матрица проверок (см. также рекомендации по проверкам Kubernetes):

Проверка Цель Не используйте
Проверка жизнеспособности TCP на PORT (по умолчанию 20128) или мягкая HTTP-проверка /healthz /api/monitoring/health
Проверка готовности HTTP GET /healthz Короткие тайм-ауты, принимающие занятый цикл событий за отказ
Глубокая / для людей /api/monitoring/health Автоматическую проверку жизнеспособности kubelet

Обновления: ожидайте разрыва каждого сеанса. По возможности предварительно отключите клиентов; при использовании SQLite по умолчанию последовательное обновление недоступно. Сочетание Compose restart: unless-stopped и Docker HEALTHCHECK также заменит единственный процесс, когда контейнер перейдёт в состояние Unhealthy, — с тем же масштабом последствий.

Фрагмент конфигурации Kubernetes для одной реплики (требуется Recreate; не увеличивайте replicas при использовании одного файла SQLite):

spec:
replicas: 1
strategy:
type: Recreate
template:
spec:
terminationGracePeriodSeconds: 90
containers:
- name: omniroute
lifecycle:
preStop:
exec:
command: ["/bin/sleep", "15"]
readinessProbe:
httpGet:
path: /healthz
port: 20128
periodSeconds: 5
livenessProbe:
tcpSocket:
port: 20128
periodSeconds: 20

Задержка preStop позволяет kube удалить конечные точки Service до SIGTERM, чтобы новый трафик перестал поступать в завершающийся процесс. Активные SSE-соединения /v1/responses обслуживаются до истечения SHUTDOWN_TIMEOUT_MS (по умолчанию 30 с) с помощью тяжеловесных разрешений на приём запросов (#11015). Новые запросы, которые всё же достигают процесса, получают 503 + Retry-After: 5. Период отсутствия конечных точек при Recreate до перехода замены в состояние Ready остаётся полной недоступностью — это свойство топологии SQLite, а не ошибка настройки проверок.

Внешний Postgres / многопроцессорная HA-конфигурация не является документированным стандартным вариантом. Если вам нужна HA, используйте одну реплику либо топологию, которую проект отдельно протестировал и задокументировал. Работа над Postgres/MySQL ведётся в #8075. Пока она не завершена, единственный поддерживаемый способ увеличить пропускную способность для крупных запросов /v1/responses — использовать N независимых процессов (см. следующий раздел), а не replicas > 1 на одном томе.

Горизонтальное масштабирование: N независимых процессов

Заголовок раздела «Горизонтальное масштабирование: N независимых процессов»

Один процесс Node — это одна куча V8. Два перекрывающихся запроса агента программирования POST /v1/responses объёмом ~3 MiB / ~750 тыс. токенов (RTK + Caveman) аварийно завершают работу этой кучи примерно при 12 Gi (FATAL ERROR: Reached heap limit) и могут вызвать OOM в cgroup размером 16 Gi. См. #7849. Этот результат измерения — предупреждение об ограничении памяти, а не жёсткий предел продукта в два одновременных длительных запроса /v1/responses. Допуск ресурсоёмких запросов чата регулируется автоматически вычисляемым бюджетом входящих байтов (OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES, src/shared/middleware/admissionBudget.ts), рассчитанным исходя из того же предела V8/cgroup. Увеличение этого значения вручную (или установка устаревшего ограничения по числу запросов OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT) в уже настроенном по размеру процессе снова приводит к аварийному завершению. Небольшие запросы чата, /healthz, /v1/models и MCP не входят в это ограничение.

Один процесс: более двух длительных запросов /v1/responses

Заголовок раздела «Один процесс: более двух длительных запросов /v1/responses»

Исправный процесс (куча ниже OMNIROUTE_CHAT_ADMISSION_HEAP_SHED_RATIO, по умолчанию 0.75) может выполнять более двух одновременных длительных запросов POST /v1/responses, если в общепроцессном бюджете байтов выполняющихся запросов (OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES / #10110) ещё остаётся место. Тела запросов размером не менее OMNIROUTE_CHAT_LARGE_BODY_BYTES (по умолчанию 256 KiB) получают ту же ресурсоёмкую аренду, что и запросы со сложной структурой, и используют тот же механизм обхода tryAcquireHealthyHeadroom из #10437 (OMNIROUTE_CHAT_ADMISSION_HEALTHY_HEADROOM). Десятки одновременных длительных SSE-клиентов (операторам часто требуется 40–50) — это вопрос бюджета памяти: необходимо подобрать размер кучи, количество основных и резервных слотов, а также OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES. Это не жёсткое ограничение продукта «не более 2». При нехватке памяти процесс по-прежнему отклоняет нагрузку с допускающим повторную попытку кодом 503, чтобы проблема #7849 не возникла снова.

Чтобы умножить количество куч (независимых старых пространств V8) сегодня:

Следует Не следует
Запускать N контейнеров/подов, каждый со своим собственным DATA_DIR / томом Устанавливать replicas > 1 для одного файла SQLite
Рассчитывать количество ресурсоёмких выполняющихся запросов и резервных слотов исходя из кучи / бюджета байтов выполняющихся запросов; 1–2 — консервативное значение по умолчанию из #7849, а не жёсткий предел продукта Выделять одному процессу в 8 раз больше RAM и снимать ограничение по количеству
Необязательно: QUOTA_STORE_DRIVER=redis + QUOTA_STORE_REDIS_URL для общих счётчиков квот Считать Redis общей SQLite — это не так
Дублировать секреты провайдеров в каждом экземпляре (или согласиться с раздельными панелями мониторинга) Ожидать одну панель мониторинга / один журнал вызовов для всех экземпляров
Размещать впереди любой балансировщик нагрузки; достаточно привязки по ключу API или сеансу Требовать специализированное промежуточное ПО конкретного поставщика с учётом размера

Оборудование: количество одновременных длительных запросов /v1/responses на экземпляр — это вопрос бюджета памяти (куча + байты выполняющихся запросов / #10110). N независимых DATA_DIR всё равно означают N куч: RAM хоста должна обеспечивать N × cgroup, а не «один под на 16 Gi с N=8». Никогда не используйте replicas > 1 для одного файла SQLite.

Пример Compose (две кучи, два тома — не deploy.replicas: 2):

services:
omniroute-a:
image: diegosouzapw/omniroute:3.8.49
environment:
DATA_DIR: /app/data
OMNIROUTE_MEMORY_MB: "12288"
QUOTA_STORE_DRIVER: redis
QUOTA_STORE_REDIS_URL: redis://redis:6379
volumes: [omniroute-a-data:/app/data]
ports: ["20128:20128"]
omniroute-b:
image: diegosouzapw/omniroute:3.8.49
environment:
DATA_DIR: /app/data
OMNIROUTE_MEMORY_MB: "12288"
QUOTA_STORE_DRIVER: redis
QUOTA_STORE_REDIS_URL: redis://redis:6379
volumes: [omniroute-b-data:/app/data]
ports: ["20138:20128"]
volumes:
omniroute-a-data:
omniroute-b-data:

Повышение плотности внутри процесса (вынесение сжатия за пределы HTTP-изолята) рассматривается в #11023. Один логический кластер с общим долговременным состоянием рассматривается в #8075.

  • Режим WAL в SQLite: необходимо дождаться завершения docker stop, чтобы OmniRoute успел записать последние изменения обратно в storage.sqlite с помощью контрольной точки. Включённые файлы Compose уже задают 40-секундный льготный период остановки. Если вы запускаете образ напрямую, используйте --stop-timeout 40.
  • DISABLE_SQLITE_AUTO_BACKUP: установите значение true, если регулярные резервные копии и резервные копии перед записью создаются внешними средствами. Для миграций существующих баз данных по-прежнему требуется отдельный надёжный снимок безопасности и защита от массовой миграции.
  • Сохранение данных: всегда подключайте том к /app/data, чтобы база данных, ключи и конфигурации сохранялись при перезапусках контейнера.
  • Настройка порта: переопределите переменную окружения PORT, чтобы изменить порт 20128, используемый по умолчанию.

Исходный код OmniRoute (a58000c7685f)

HagiCode

HagiCode — агентная среда разработки со структурированными процессами, параллельным выполнением несколькими агентами и интерфейсами Hero Dungeon.

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

Главный экран HagiCode в светлой теме
  • SmartСтруктурированные процессы превращают намерение в исполнимый путь от идеи до готового изменения.
  • EfficientМультиагентные процессы параллельно продвигают исследование, реализацию и проверку.
  • FunHero Dungeon делает длительную совместную разработку наглядной и увлекательной.
Перейти на HagiCode