Ir al contenido
OmniRoute source

🐳 Docker Guide — OmniRoute (Español)

¿Autoalojar con un solo comando? Consulta la Guía de autoalojamiento — docker compose -f docker-compose.selfhost.yml up -d (imagen publicada + Redis, solo en la interfaz de bucle invertido, sin elección de perfil). La ejecución rápida que aparece a continuación es la opción de contenedor único para usuarios que ya ejecutan Redis en otro lugar.

Ventana de terminal
docker run -d \
--name omniroute \
--restart unless-stopped \
--stop-timeout 40 \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
Ventana de terminal
# Primero, copia y edita .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
Ventana de terminal
# Perfil base (sin herramientas de CLI)
docker compose --profile base up -d
# Perfil de CLI (Claude Code, Codex y OpenClaw integrados)
docker compose --profile cli up -d
# Perfil de host (orientado principalmente a Linux; monta los binarios de CLI del host en modo de solo lectura)
docker compose --profile host up -d
# Perfil web (Chromium/Playwright para proveedores de sesiones web)
docker compose --profile web up -d
# Combinar CLI + servicio auxiliar CLIProxyAPI
docker compose --profile cli --profile cliproxyapi up -d

OmniRoute incluye perfiles de Compose para las principales modalidades de implementación. Elige el que se adapte a tu entorno.

Perfil Servicio Cuándo usarlo Comando
base (predeterminado) omniroute-base Servidor sin interfaz gráfica / entorno de ejecución mínimo, sin herramientas CLI de proveedores incluidas docker compose --profile base up -d
cli omniroute-cli Flujos de trabajo con agentes que llaman a omniroute providers/setup/doctor y herramientas CLI incluidas (Codex, Claude Code, Droid, OpenClaw) docker compose --profile cli up -d
host omniroute-host Hosts Linux que necesitan un acceso similar a network_mode a las herramientas CLI del host montando ~/.local/bin, ~/.codex, ~/.claude, etc. en modo de solo lectura docker compose --profile host up -d
cliproxyapi cliproxyapi Ejecuta el contenedor auxiliar CLIProxyAPI en el puerto 8317 para el proxy de herramientas CLI ascendentes docker compose --profile cliproxyapi up -d
web omniroute-web Proveedores de sesiones web que necesitan un navegador: gemini-web, claude-web, claude-turnstile (compila runner-web, incluye Chromium) docker compose --profile web up -d

Se pueden combinar varios perfiles: docker compose --profile cli --profile cliproxyapi up -d.

Configuración de herramientas CLI del host cuando OmniRoute se ejecuta en Docker

Sección titulada «Configuración de herramientas CLI del host cuando OmniRoute se ejecuta en Docker»

omniroute setup-codex, setup-claude, config set <tool> y el botón Guardar configuración del panel escriben archivos como ~/.codex/*.config.toml. Esas rutas solo tienen significado en la máquina donde realmente se ejecuta la CLI. Si los ejecuta dentro del contenedor, la escritura se realiza en el directorio de inicio del propio contenedor (/home/node — la imagen se ejecuta con USER node), donde ninguna CLI del host los leerá jamás y donde se descartan en cuanto se vuelve a crear el contenedor.

OmniRoute detecta esta situación y rechaza la escritura mostrando instrucciones, en lugar de informar de un éxito que no puede aprovechar: la CLI finaliza con el código 2 y la API responde con 422 y containerEphemeralTarget: true.

Recomendado: ejecute la CLI en el host y OmniRoute en Docker

Sección titulada «Recomendado: ejecute la CLI en el host y OmniRoute en Docker»

El contenedor proporciona la API; la CLI configura sus herramientas del host.

Ventana de terminal
docker compose --profile base up -d
npm install -g omniroute
omniroute connect http://localhost:20128 # dirige la CLI al contenedor
omniroute setup-codex # escribe en el ~/.codex real de su host

Esta es la opción adecuada cuando Codex, Claude Code, Cursor o herramientas similares se ejecutan en su portátil, que es la configuración habitual.

Alternativa: monte mediante bind los directorios de configuración del host (perfil host)

Sección titulada «Alternativa: monte mediante bind los directorios de configuración del host (perfil host)»

Si desea que el propio contenedor escriba la configuración del host, monte los directorios y haga que CLI_CONFIG_HOME apunte a la raíz del montaje. El perfil host ya hace esto:

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

Un montaje bind es lo que hace que la ruta sea fiable: OmniRoute lee /proc/self/mountinfo y permite escribir en rutas montadas (y en directorios cuyos elementos secundarios sean montajes, que es exactamente la estructura de /host-home anterior), mientras sigue rechazando las que no están montadas.

Vía de escape: configure las propias CLI del contenedor (úsela con moderación)

Sección titulada «Vía de escape: configure las propias CLI del contenedor (úsela con moderación)»

Cuando las CLI residen realmente dentro del contenedor (el perfil cli), la escritura es intencionada. Pase --allow-container-write a cualquier comando setup-* o establezca OMNIROUTE_ALLOW_CONTAINER_CONFIG_WRITE=true para el servidor. La escritura se realiza con una advertencia de que no sobrevivirá al contenedor.

Advertencia de seguridad: perfil cli + montaje de docker.sock. El perfil cli monta mediante bind /var/run/docker.sock para que el actualizador automático del contenedor pueda volver a crear la pila mediante el daemon del host (src/lib/system/autoUpdate.ts comprueba la existencia de ese socket y omite la ruta de Docker cuando no está presente). Ese socket es un límite de confianza con privilegios root en el host: cualquier cosa que pueda acceder a él controla el daemon de Docker del host como root; puede crear, inspeccionar, detener y eliminar cualquier contenedor del host. Implicaciones:

  1. Nunca exponga a la red el puerto del perfil cli. Publíquelo en 127.0.0.1 (ports: "127.0.0.1:${DASHBOARD_PORT:-20128}:...") — un perfil cli accesible desde la LAN convierte cualquier RCE en el panel en un compromiso total del host.
  2. No monte mediante bind ningún directorio adicional del host en el perfil cli. El socket de Docker junto con cualquier montaje adicional proporciona al contenedor acceso completo de lectura y escritura a su sistema de archivos y a la configuración del host. Si necesita que una herramienta vea un proyecto, ejecútela localmente con el binario de la CLI; no lo monte en el contenedor cli.

Si no necesita actualizaciones automáticas dentro del contenedor, deje desactivado el perfil cli (COMPOSE_PROFILES=core,redis o una opción más corta). Los demás perfiles no montan el socket de Docker.

Consulte docs/security/MITM-TPROXY-DECRYPT.md (en git; no se compila en /docs) para ver el modelo de amenazas relacionado con MITM, y docs/security/SUPPLY_CHAIN.md para conocer la cadena de procedencia de los binarios codex/claude-code/droid/openclaw.

OmniRoute utiliza Redis como respaldo para el limitador de velocidad distribuido y la caché compartida. El servicio redis está siempre definido en docker-compose.yml (no está condicionado por ningún perfil) y se inicia junto con cualquier otro perfil.

Detalle Valor
Imagen redis:7-alpine
Nombre del contenedor omniroute-redis
Puerto interno 6379
Puerto del host (anulable) REDIS_PORT (valor predeterminado: 6379)
Enlace al host (anulable) REDIS_BIND_HOST (valor predeterminado: 127.0.0.1)
Volumen omniroute-redis-data → /data
Comprobación de estado redis-cli ping (intervalo de 10 s)

Variables de entorno relacionadas:

  • REDIS_URL — cadena de conexión inyectada en la aplicación (redis://redis:6379 de forma predeterminada).
  • REDIS_PORT — asignación del puerto del host para el contenedor de Redis.
  • REDIS_BIND_HOST — interfaz del host en la que se publica el puerto. El valor predeterminado es 127.0.0.1.

Por qué se usa la interfaz de bucle invertido de forma predeterminada: el sidecar se ejecuta sin requirepass, y los contenedores de la aplicación acceden a él a través de la red de Compose (redis:6379); el puerto publicado solo está disponible para herramientas del host (redis-cli, un npm run dev local). Publicarlo en 0.0.0.0 expondría un Redis sin autenticación a todos los hosts de la red LAN. Si establece REDIS_BIND_HOST=0.0.0.0, añada también --requirepass al campo command: del servicio.

No se recomienda deshabilitar Redis (el limitador de velocidad pasará a usar un mecanismo alternativo en memoria). Si es necesario, elimine o comente el bloque del servicio redis: en docker-compose.yml, o escálelo a cero:

Ventana de terminal
docker compose up -d --scale redis=0

Para ejecutar una instancia aislada de producción junto al entorno de desarrollo, utilice docker-compose.prod.yml.

Detalle Valor
Archivo docker-compose.prod.yml
Puerto predeterminado del panel PROD_DASHBOARD_PORT=20130 (asignado internamente a ${DASHBOARD_PORT:-20128})
Puerto predeterminado de la API PROD_API_PORT=20131
Imagen omniroute:prod (compilada desde el objetivo runner-cli)
Contenedor de Redis omniroute-redis-prod (redis:8.6.2, volumen dedicado redis-prod-data)
Volumen de datos omniroute-prod-data (con nombre y persistente entre reconstrucciones)
Comprobaciones de estado node healthcheck.mjs + redis-cli ping, con depends_on condicionado al estado de Redis

Cómo utilizarlo:

Ventana de terminal
# Compilar e iniciar la pila de producción
docker compose -f docker-compose.prod.yml up -d --build
# Transmitir los registros
docker compose -f docker-compose.prod.yml logs -f
# Detener y eliminar la pila (conservar los volúmenes)
docker compose -f docker-compose.prod.yml down

La pila de producción se ejecuta en paralelo con la configuración de Compose de desarrollo (utiliza nombres de contenedores, puertos y volúmenes diferentes), por lo que puede seguir realizando cambios localmente mientras producción permanece activa.

El repositorio incluye un Dockerfile multietapa (Dockerfile). Se exponen cuatro etapas; elige el target adecuado para tu caso de uso.

Etapa Imagen base Propósito
builder node:26-trixie-slim Instala las dependencias (npm ci --legacy-peer-deps) y ejecuta npm run build (Turbopack de forma predeterminada; consulta Recursos de compilación más abajo)
runner-base node:26-trixie-slim Entorno de ejecución de producción con la salida independiente de Next.js. No incluye las CLI de los proveedores.
runner-cli runner-base Añade git, docker.io, docker-compose y las CLI globales: @openai/codex, @anthropic-ai/claude-code, droid, openclaw. Elige esta opción para flujos de trabajo con agentes.
runner-web runner-base Añade Playwright y un navegador Chromium (--with-deps) para proveedores de sesiones web: gemini-web, claude-web, claude-turnstile. Elige esta opción cuando uses esos proveedores; la imagen básica falla al recibir solicitudes si no los incluye (consulta la nota sobre -web en Canales de lanzamiento).

Compila manualmente un objetivo específico:

Ventana de terminal
docker build --target runner-base -t omniroute:base .
docker build --target runner-cli -t omniroute:cli .
docker build --target runner-web -t omniroute:web .

Tres argumentos de compilación controlan el consumo de recursos de la etapa builder. Solo se aplican durante la compilación; OMNIROUTE_MEMORY_MB (más abajo) es un parámetro independiente del entorno de ejecución.

Argumento de compilación Valor predeterminado Efecto
OMNIROUTE_USE_TURBOPACK 1 0 compila con webpack en su lugar. Menor pico de memoria, pero más lento.
OMNIROUTE_BUILD_MEMORY_MB 6144 Límite del montículo de V8 (--max-old-space-size) para el proceso next build generado.
OMNIROUTE_BUILD_WORKERS 2 Proporciona el valor de CIRCLE_NODE_TOTAL; Next deriva workers = N - 1 para recopilar datos de páginas.

OMNIROUTE_BUILD_WORKERS es el parámetro que debes aumentar en un sistema de compilación potente y el que debes investigar cuando una compilación con recursos limitados muere después de ✓ Compiled successfully. Cada proceso de trabajo de datos de páginas es un proceso independiente, al igual que el propio proceso principal next build; una reproducción en un VPS real (incidencia #7518) midió el pico de RSS de cada proceso en ~4.5 GB, independientemente de la opción de montículo NODE_OPTIONS (Turbopack compila en memoria nativa/Rust fuera del montículo de V8). El valor predeterminado de 2 (→ 1 proceso de trabajo, 2 procesos en total) está dimensionado para los ejecutores alojados en GitHub de 16 GB/4 vCPU que utiliza la canalización de publicación. Con 8 (→ 7 procesos de trabajo), ese ejecutor se quedó sin memoria y buildkit hizo que el paso fallara con ResourceExhausted: ... cannot allocate memory; 3 (→ 2 procesos de trabajo) tampoco cabía una vez que el RSS por proceso se midió directamente en vez de inferirse. tests/unit/docker-build-memory-budget.test.ts realiza los cálculos con respecto a la cifra medida y falla si cualquiera de los parámetros supera la capacidad del ejecutor.

Turbopack compila en memoria nativa de Rust que reside fuera del montículo de V8, por lo que OMNIROUTE_BUILD_MEMORY_MB no la limita. En un host con un límite de memoria, el OOM killer finaliza entonces la compilación mediante SIGKILL sin mostrar ningún texto de error: simplemente se detiene durante Creating an optimized production build, lo que parece un bloqueo en lugar de una falta de memoria. Si el host de compilación tiene recursos limitados, cambia de empaquetador:

Ventana de terminal
docker build --target runner-base \
--build-arg OMNIROUTE_USE_TURBOPACK=0 \
-t omniroute:base .

webpackBuildWorker está habilitado, por lo que next build ejecuta un proceso principal y un proceso de trabajo, y cada uno respeta OMNIROUTE_BUILD_MEMORY_MB por separado. Establece el límite del contenedor por encima de aproximadamente el doble de ese valor, no una sola vez.

Mediciones en este árbol (--target runner-base, OMNIROUTE_BUILD_MEMORY_MB=6144):

Empaquetador Límite del contenedor Resultado
Turbopack 8 GiB / 16 GiB finalizado por OOM en ambos, sin avisos
webpack 8 GiB proceso de trabajo finalizado por SIGKILL
webpack 12 GiB correcto, con un pico de 11.1 GiB

Valores predeterminados del entorno de ejecución

Sección titulada «Valores predeterminados del entorno de ejecución»

Valores predeterminados exportados por 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.

Comportamiento de la memoria en Docker:

  • La imagen establece OMNIROUTE_MEMORY_MB=1024 y deriva NODE_OPTIONS=--max-old-space-size=1024 a partir de ese valor.
  • El proceso real del servidor se inicia mediante el lanzador independiente, que lee OMNIROUTE_MEMORY_MB y añade --max-old-space-size=<OMNIROUTE_MEMORY_MB>.
  • Node utiliza el último valor repetido de --max-old-space-size, por lo que establecer OMNIROUTE_MEMORY_MB controla el límite efectivo del heap en Docker.
  • Como la imagen siempre lo establece, el mecanismo alternativo del lanzador, calibrado según la RAM, nunca se aplica en Docker. Auméntelo explícitamente según la carga de trabajo (tabla siguiente). 2048 sigue siendo demasiado poco para /v1/responses de agentes de programación.

RAM en tiempo de ejecución para agentes de programación

Sección titulada «RAM en tiempo de ejecución para agentes de programación»

El valor predeterminado de Docker de 1 GiB es un mínimo para el panel y chats ligeros, no una configuración para producción. Los cuerpos largos de POST /v1/responses (cientos de mensajes, decenas de herramientas) mantienen varios grafos en memoria durante la compresión. Dos solicitudes simultáneas de ~3 MiB / ~750k tokens han provocado la interrupción de V8 con un old-space de 12 GiB (FATAL ERROR: Reached heap limit) y también han alcanzado el OOM de un cgroup de 16 GiB. Consulte #7849.

Dimensione la --memory del cgroup por encima del heap: los búferes nativos, SQLite y los datos intermedios de compresión se encuentran fuera de V8.

Carga de trabajo OMNIROUTE_MEMORY_MB Contenedor / cgroup Notas
Panel, un chat ligero 1024 (valor predeterminado de la imagen) ≥2 GiB
Un agente de programación (Claude/Codex/Grok) 8192 ≥10 GiB Sesión única típica de /v1/responses
Dos solicitudes largas simultáneas de /v1/responses 10240–12288 ≥12–16 GiB Interrupción de V8 observada con un heap de ~12 GiB
Tres o más contextos largos simultáneos no usar en un solo proceso serializar / más RAM El límite de admisión predeterminado para cargas pesadas es de 1 solicitud en curso; aumentarlo sin más RAM vuelve a provocar la interrupción

omniroute serve en un sistema sin contenedores calibra aproximadamente el 35 % de la RAM (limitado al intervalo [512, 4096]) cuando OMNIROUTE_MEMORY_MB no está establecida. Docker siempre establece 1024, por lo que esa calibración nunca se ejecuta en la imagen oficial.

Ventana de terminal
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

Además de los valores predeterminados documentados en ENVIRONMENT.md, las siguientes variables son las más importantes al ejecutar la aplicación en Docker:

Variable Propósito Valor predeterminado
OMNIROUTE_WS_BRIDGE_SECRET Secreto compartido para el puente WebSocket. Obligatorio en producción — establézcalo como una cadena aleatoria segura. sin definir (debe proporcionarse)
REDIS_URL Cadena de conexión para el backend del limitador de solicitudes y la caché redis://redis:6379
REDIS_PORT Puerto del host para el contenedor Redis incluido 6379
REDIS_BIND_HOST Interfaz del host en la que se publica el puerto de Redis incluido (bucle invertido, a menos que añada AUTH) 127.0.0.1
AUTO_UPDATE_HOST_REPO_DIR Ruta del host montada en el perfil cli en /workspace/omniroute para los flujos de trabajo de actualización automática . (directorio actual)
OMNIROUTE_MEMORY_MB Límite del heap de Node en tiempo de ejecución para el servidor independiente de Docker; sustituye el valor predeterminado de la imagen indicado anteriormente. Agentes de programación: 8192 o más (consulte la RAM en tiempo de ejecución). 1024
DASHBOARD_PORT / API_PORT Sustituye los puertos expuestos del panel (20128) y de la API (20129) 20128 / 20129
APP_BIND_HOST Interfaz del host en la que docker-compose publica los puertos del panel, la API y WebSocket en vivo. Con REQUIRE_API_KEY=false (valor predeterminado), 0.0.0.0 expone el proxy anónimo /v1 a la LAN; amplíe el acceso únicamente con REQUIRE_API_KEY=true o un proxy inverso delante. 127.0.0.1
CLIPROXY_BIND_HOST Interfaz del host en la que docker-compose publica el servicio auxiliar cliproxyapi; su volumen de datos almacena las credenciales del proveedor. 127.0.0.1
OMNIROUTE_PLUGINS_DIR Directorio que el analizador de plugins en tiempo de ejecución lee y en el que instala. Configúrelo cuando los plugins se monten mediante bind mount: el valor predeterminado depende de HOME, que una imagen no necesariamente exporta. ~/.omniroute/plugins
OMNIROUTE_BASE_PATH Subruta de URL cuando la aplicación se publica detrás de un proxy inverso (p. ej., /omniroute) (vacío = raíz)
NEXT_PUBLIC_BASE_URL Origen público del navegador, incluida la subruta (p. ej., https://host/omniroute) sin definir
PROD_DASHBOARD_PORT Puerto del panel en el host para docker-compose.prod.yml 20130
CLIPROXYAPI_PORT Puerto del host para el servicio auxiliar cliproxyapi 8317

Proxy inverso en una subruta (Traefik / nginx)

Sección titulada «Proxy inverso en una subruta (Traefik / nginx)»

El basePath de Next.js se compila en el paquete independiente. OmniRoute registra el valor integrado en un archivo centinela en la raíz de la aplicación (escrito durante npm run build; leído por scripts/docker/ensure-docker-base-path.mjs) y lo compara con OMNIROUTE_BASE_PATH cuando se inicia el contenedor. Cuando son diferentes y la imagen se compiló para la raíz del dominio, el punto de entrada reescribe los manifiestos independientes, los literales basePath/assetPrefix integrados (Next 16 genera las URL de los recursos SSR únicamente a partir de assetPrefix; el parcheador replica la subruta en él), las URL de recursos /_next/static integradas (manifiestos de referencias del cliente, importaciones multimedia, páginas de error prerenderizadas) y el shim process.env del cliente antes de que se ejecute node dev/run-standalone.mjs.

Establezca ambas variables en .env y, a continuación, vuelva a compilar para que la imagen y el entorno de ejecución coincidan:

.env
OMNIROUTE_BASE_PATH=/omniroute
NEXT_PUBLIC_BASE_URL=https://myhostname.example.com/omniroute
Ventana de terminal
docker compose --profile base up -d --build

docker-compose.yml reenvía OMNIROUTE_BASE_PATH como argumento de compilación de Docker y como variable de entorno en tiempo de ejecución.

Imagen raíz precompilada + subruta en tiempo de ejecución

Sección titulada «Imagen raíz precompilada + subruta en tiempo de ejecución»

Las imágenes publicadas diegosouzapw/omniroute:* están compiladas para la raíz del dominio. Aun así, puede establecer OMNIROUTE_BASE_PATH en tiempo de ejecución; el contenedor parchea el paquete una vez durante el inicio. Combínelo con el origen público correspondiente:

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

Configure el proxy inverso para que reenvíe la ruta externa completa (no elimine el prefijo). Traefik debe dirigir PathPrefix(/omniroute) al contenedor sin StripPrefix, de modo que Next.js reciba /omniroute/... y sirva los recursos desde /omniroute/_next/....

La comprobación de estado de Docker consulta el endpoint ligero de ciclo de vida /healthz, con el prefijo del OMNIROUTE_BASE_PATH activo. /api/monitoring/health sigue estando disponible para diagnósticos humanos o mediante paneles; para hacer que el HEALTHCHECK del contenedor vuelva a apuntar a él (por ejemplo, para aplicar una comprobación de estado exhaustiva), establezca OMNIROUTE_HEALTHCHECK_PATH=/api/monitoring/health. Esa ruta es una comprobación exhaustiva (base de datos + resumen de monitorización), apropiada para el HEALTHCHECK poco frecuente de Docker si decide volver a habilitarla, pero no para los intervalos de livenessProbe de Kubernetes.

Para orquestadores (Kubernetes, Nomad, etc.):

Sonda Preferir Evitar
Actividad HTTP GET /livez, o TCP en el puerto principal (PORT, valor predeterminado 20128) /api/monitoring/health como comprobación de actividad
Disponibilidad HTTP GET /healthz Tiempos de espera estrictos que interpreten un bucle de eventos ocupado como inactivo
Profunda / caja negra /api/monitoring/health —

/healthz informa del ciclo de vida del proceso (ok / starting / stopping). /livez solo comprueba que el proceso esté activo (devuelve 200 siempre que el controlador pueda ejecutarse; no espera a que esté disponible). Ambos siguen ejecutándose en el mismo bucle de eventos de Node que gestiona las solicitudes, por lo que el trabajo de catálogo o compresión que hace un uso intensivo de la CPU puede retrasarlos: ocupado ≠ inactivo. Prefiera comprobaciones de actividad mediante TCP si las sondas HTTP agotan el tiempo de espera. Guía completa sobre sondas: Guía de monitorización — recomendaciones de sondas de Kubernetes.

OmniRoute puede exponerse de forma segura mediante el aprovisionamiento automático de SSL de Caddy. Asegúrate de que el registro A de DNS de tu dominio apunte a la IP de tu servidor.

services:
omniroute:
image: diegosouzapw/omniroute:latest
container_name: omniroute
restart: unless-stopped
volumes:
- omniroute-data:/app/data
environment:
- PORT=20128
# Origen visible para el navegador usado en callbacks de OAuth, enlaces del panel y URLs públicas generadas.
- NEXT_PUBLIC_BASE_URL=https://your-domain.com
# URL interna de servidor a servidor para tareas programadas y solicitudes a sí mismo.
- 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 establece las cabeceras de reenvío estándar para el contenedor ascendente. OmniRoute utiliza NEXT_PUBLIC_BASE_URL como origen público canónico para los callbacks de OAuth y los enlaces públicos generados; las escrituras autenticadas del panel utilizan solicitudes del mismo origen junto con protección CSRF vinculada a la sesión. Habilita OMNIROUTE_TRUST_PROXY únicamente en implementaciones avanzadas en las que quieras que OmniRoute derive el origen público de cabeceras de reenvío de confianza en lugar de una configuración explícita.

La compatibilidad del panel con implementaciones de Docker incluye un túnel rápido de Cloudflare con un solo clic en Dashboard → Endpoints. La primera activación descarga cloudflared únicamente cuando es necesario, inicia un túnel temporal hacia tu endpoint /v1 actual y muestra la URL https://*.trycloudflare.com/v1 generada directamente debajo de tu URL pública normal.

Los paneles de túneles de endpoints (Cloudflare, Tailscale, ngrok) pueden mostrarse u ocultarse desde Settings → Appearance sin cambiar el estado activo del túnel.

  • Las URLs de los túneles rápidos son temporales y cambian después de cada reinicio.
  • Los túneles rápidos no se restauran automáticamente después de reiniciar OmniRoute o el contenedor. Vuelve a habilitarlos desde el panel cuando sea necesario.
  • Actualmente, la instalación administrada es compatible con Linux, macOS y Windows en x64 / arm64.
  • Los túneles rápidos administrados utilizan de forma predeterminada el transporte HTTP/2 para evitar advertencias molestas sobre el búfer UDP de QUIC en entornos de contenedores con recursos limitados. Establece CLOUDFLARED_PROTOCOL=quic o auto si quieres utilizar un transporte diferente.
  • Las imágenes de Docker incluyen los certificados de CA raíz del sistema y los proporcionan a la instancia administrada de cloudflared, lo que evita errores de confianza TLS cuando el túnel se inicializa dentro del contenedor.
  • Establece CLOUDFLARED_BIN=/absolute/path/to/cloudflared si quieres que OmniRoute utilice un binario existente en lugar de descargar uno.
Imagen Etiqueta Tamaño Descripción
diegosouzapw/omniroute latest ~250MB Versión estable SemVer publicada más alta (no main de git)
diegosouzapw/omniroute 3.8.0 ~250MB Fije este tipo de etiqueta para GitOps

Manifiesto multiplataforma: linux/amd64 + linux/arm64 nativos (Apple Silicon, AWS Graviton, Raspberry Pi). Docker selecciona automáticamente la arquitectura correspondiente; utilice --platform linux/amd64 si necesita forzar la emulación de AMD64 en hosts ARM.

OmniRoute publica canales de Docker separados para versiones estables, pruebas de la rama de publicación activa y compilaciones de desarrollo.

Canal Origen Mutabilidad Uso recomendado
:&lt;version&gt; / :&lt;version&gt;-web Versión firmada/versionada Inmutable Despliegues de producción que fijan una versión exacta
:latest / :latest-web Versión estable SemVer publicada más alta Puntero estable mutable Sigue las versiones estables después de un trabajo de publicación SemVer; no sigue main ni confirmaciones release/v* no publicadas
:next / :next-web Rama release/v* predeterminada actual Puntero de prelanzamiento mutable Prueba de correcciones que se han incorporado a la rama de publicación activa, pero que aún no están en una versión estable
:main / :main-web Rama main Puntero de desarrollo mutable Solo para pruebas de desarrollo e integración

Proveedores de sesiones web: las imágenes -web

Sección titulada «Proveedores de sesiones web: las imágenes -web»

Cada canal anterior también está disponible como una etiqueta -web (:latest-web, :&lt;version&gt;-web, :next-web, :main-web), creada a partir de la etapa runner-web: la misma imagen, pero con Playwright y un navegador Chromium. La imagen normal se distribuye sin Chromium; gemini-web, claude-web y claude-turnstile lo necesitan.

El error se pospone hasta la primera solicitud, no se produce al iniciar: esos proveedores muestran sus modelos y aparecen como conectados en el panel, y solo la primera solicitud falla con

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

Si utiliza esos proveedores, descargue la etiqueta -web del canal que ya esté usando; no cambia nada más. En una instalación mediante npm/CLI (sin imagen de Docker), el componente que falta es el binario del navegador: ejecute npx playwright install chromium en el host.

El canal next se vuelve a compilar con cada inserción en la rama release/v* predeterminada actual y se publica tanto para AMD64 como para ARM64. Las ramas de mantenimiento más antiguas no pueden sobrescribirlo. El canal proporciona una imagen descargable para las correcciones que se hayan fusionado en la rama de publicación activa antes de crear la siguiente etiqueta estable.

Ventana de terminal
docker pull diegosouzapw/omniroute:next
docker pull diegosouzapw/omniroute:next-web

Para Docker Compose, sustituya la etiqueta de imagen utilizada por el perfil seleccionado y, a continuación, descargue y vuelva a crear el servicio:

services:
omniroute:
image: diegosouzapw/omniroute:next
Ventana de terminal
docker compose pull
docker compose up -d

next es un canal de prelanzamiento flotante. Puede cambiar con cualquier inserción en la rama de publicación activa y no está admitido para uso en producción. Fije el resumen de la imagen mientras evalúa una compilación específica:

Ventana de terminal
docker pull diegosouzapw/omniroute:next
docker image inspect diegosouzapw/omniroute:next --format '{{index .RepoDigests 0}}'

Antes de realizar pruebas, haga una copia de seguridad del volumen de datos de OmniRoute o del directorio de datos montado mediante vinculación. Para revertir, restaure la versión estable o el resumen utilizados anteriormente y vuelva a crear el contenedor:

Ventana de terminal
docker pull diegosouzapw/omniroute:&lt;stable-version&gt;
docker compose up -d

Una compilación de una rama de publicación nunca puede mover latest; solo una versión semántica estable apta puede promocionar el puntero estable. Las imágenes next conservan la inspección de imágenes de publicación y la barrera de bloqueo de vulnerabilidades CRÍTICAS.

latest no garantiza la actualidad con respecto a git. Las correcciones fusionadas en main o en la rama release/v* activa no se incluyen en :latest hasta que se publica una imagen SemVer estable y el trabajo de publicación promociona :latest (con el mismo resumen que esa versión SemVer). Si latest parece congelada mientras GitHub ya muestra la corrección, descargue :next para probar la rama de publicación o espere a la etiqueta SemVer.

Lo que desea Utilice
GitOps / producción que no debe desviarse Fije :X.Y.Z (o el resumen de la imagen)
Seguir las versiones estables publicadas y aceptar una recreación en cada versión :latest
Probar confirmaciones release/v* no publicadas :next (no para producción)
Probar main :main (no para producción)

Disponibilidad: SQLite predeterminado admite una sola réplica

Sección titulada «Disponibilidad: SQLite predeterminado admite una sola réplica»

La configuración estándar de OmniRoute en Docker / Kubernetes consiste en un proceso de Node + un escritor de SQLite. La alta disponibilidad no es compatible con esta topología.

Restricción Consecuencia
Escritor único No ejecute varias réplicas contra el mismo archivo de SQLite. Esto corrompe la base de datos.
Recreación / reinicio / terminación por HEALTHCHECK Interrupción total de las conexiones SSE en curso, las sesiones del panel y el estado en memoria. Todos los clientes conectados se desconectan. Las solicitudes nuevas durante el intervalo sin endpoints reciben del proxy inverso un 502 Bad Gateway: Unknown error, no JSON de OmniRoute; los clientes no pueden distinguirlo de un fallo del proveedor (#11015).
Mismo bucle de eventos que /healthz Un ciclo ocupado del catálogo o de compresión puede retrasar las sondas; un tiempo de espera corto reiniciará entonces la única réplica.

Matriz de sondas (consulte también Recomendaciones de sondas de Kubernetes):

Sonda Objetivo No usar
Supervivencia TCP en PORT (predeterminado: 20128) o HTTP flexible en /healthz /api/monitoring/health
Disponibilidad HTTP GET /healthz Tiempos de espera estrictos que interpreten un bucle de eventos ocupado como inactivo
Profunda / para humanos /api/monitoring/health Comprobación de supervivencia automatizada de kubelet

Actualizaciones: espere que todas las sesiones se desconecten. Drene los clientes si puede; no existe una actualización gradual con el SQLite predeterminado. La combinación de restart: unless-stopped de Compose y HEALTHCHECK de Docker también sustituirá el único proceso cuando el contenedor esté Unhealthy, con el mismo alcance de impacto.

Fragmento de Kubernetes para una sola réplica (se requiere Recreate; no aumente replicas contra un único archivo de 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

La espera de preStop permite que kube retire los endpoints del Service antes de SIGTERM, para que el tráfico nuevo deje de llegar al proceso que está finalizando. Las conexiones SSE de /v1/responses en curso se drenan durante un máximo de SHUTDOWN_TIMEOUT_MS (30 s de forma predeterminada) mediante concesiones de admisión de peso pesado (#11015). Las solicitudes nuevas que aún lleguen al proceso reciben 503 + Retry-After: 5. El intervalo de Recreate sin endpoints hasta que el reemplazo esté Ready sigue siendo una interrupción total; esto se debe a la topología de SQLite, no a una configuración incorrecta de las sondas.

Postgres externo / HA con múltiples escritores no es una opción estándar documentada. Si necesita HA, mantenga una sola réplica o ejecute una topología que el proyecto haya probado y documentado por separado. El trabajo relacionado con Postgres/MySQL se encuentra en #8075. Hasta que esté disponible, la única forma compatible de multiplicar la capacidad de solicitudes grandes a /v1/responses es usar N procesos independientes (sección siguiente), no replicas > 1 sobre un mismo volumen.

Escalado horizontal: N procesos independientes

Sección titulada «Escalado horizontal: N procesos independientes»

Un proceso de Node equivale a un heap de V8. Dos solicitudes superpuestas de agente de programación POST /v1/responses de ~3 MiB / ~750k tokens (RTK + Caveman) provocan la interrupción de ese heap al alcanzar ~12 Gi (FATAL ERROR: Reached heap limit) y pueden causar un OOM en un cgroup de 16 Gi. Consulta #7849. Esa medición es una advertencia sobre el presupuesto de memoria, no un máximo estricto del producto de dos solicitudes largas concurrentes a /v1/responses. La admisión de chats pesados está controlada por un presupuesto de bytes de entrada derivado automáticamente (OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES, src/shared/middleware/admissionBudget.ts), dimensionado a partir de ese mismo límite de V8/cgroup; aumentarlo manualmente (o establecer el límite heredado por número de solicitudes OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT) en un proceso ya dimensionado vuelve a introducir la interrupción. Los chats pequeños, /healthz, /v1/models y MCP no están incluidos en ese límite.

Un proceso: más de dos solicitudes largas a /v1/responses

Sección titulada «Un proceso: más de dos solicitudes largas a /v1/responses»

Un proceso saludable (con el heap por debajo de OMNIROUTE_CHAT_ADMISSION_HEAP_SHED_RATIO, cuyo valor predeterminado es 0.75) puede ejecutar más de dos solicitudes largas concurrentes POST /v1/responses cuando aún queda espacio en el presupuesto de bytes en curso de todo el proceso (OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES / #10110). Los cuerpos con un tamaño igual o superior a OMNIROUTE_CHAT_LARGE_BODY_BYTES (256 KiB de forma predeterminada) adquieren la misma concesión para cargas pesadas que las solicitudes con estructuras complejas y utilizan la misma vía de escape tryAcquireHealthyHeadroom de #10437 (OMNIROUTE_CHAT_ADMISSION_HEALTHY_HEADROOM). Tener decenas de clientes SSE largos concurrentes (los operadores suelen necesitar entre 40 y 50) es una cuestión de presupuesto de memoria —dimensiona el heap, las ranuras principales/de margen y OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES—, no un límite estricto del producto de «máximo 2». Un heap bajo presión continúa rechazando carga con un 503 reintentable para evitar que se repita #7849.

Para multiplicar los heaps (espacios antiguos de V8 independientes) hoy:

Haz No hagas
Ejecuta N contenedores/pods, cada uno con su propio DATA_DIR / volumen Establecer replicas > 1 sobre un único archivo SQLite
Dimensiona las solicitudes pesadas en curso y el margen saludable según el presupuesto de heap / bytes en curso; 1–2 es el valor predeterminado conservador de #7849, no un máximo estricto del producto Asignar a un proceso 8 veces más RAM y un límite de cantidad ilimitado
Opcional: QUOTA_STORE_DRIVER=redis + QUOTA_STORE_REDIS_URL para disponer de contadores de cuota compartidos Tratar Redis como SQLite compartido: no lo es
Duplica los secretos de los proveedores en cada instancia (o acepta paneles particionados) Esperar un único panel / registro de llamadas entre instancias
Coloca cualquier balanceador de carga delante; la afinidad por clave de API o sesión es suficiente Requerir middleware específico de un proveedor que tenga en cuenta el tamaño

Hardware: la cantidad de solicitudes largas concurrentes a /v1/responses por instancia es una cuestión de presupuesto de memoria (heap + bytes en curso / #10110). N valores de DATA_DIR independientes siguen multiplicando los heaps: la RAM del host debe cubrir N × cgroup, no «un pod de 16 Gi con N=8». Nunca uses replicas > 1 sobre un único archivo SQLite.

Borrador de Compose (dos heaps, dos volúmenes; no 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:

La densidad dentro del proceso (compresión fuera del aislamiento HTTP) se aborda en #11023. Un clúster lógico sobre estado persistente compartido se aborda en #8075.

  • Modo WAL de SQLite: Se debe permitir que docker stop finalice para que OmniRoute pueda guardar los cambios más recientes en storage.sqlite mediante un punto de control. Los archivos de Compose incluidos ya establecen un período de gracia de detención de 40 s. Si ejecuta la imagen directamente, mantenga --stop-timeout 40.
  • DISABLE_SQLITE_AUTO_BACKUP: Establézcalo en true si las copias de seguridad rutinarias/previas a la escritura se administran externamente. Las migraciones de bases de datos existentes siguen requiriendo su propia instantánea de seguridad duradera y una protección contra migraciones masivas.
  • Persistencia de datos: Monte siempre un volumen en /app/data para conservar la base de datos, las claves y las configuraciones entre reinicios del contenedor.
  • Configuración del puerto: Sobrescriba la variable de entorno PORT para cambiar el puerto predeterminado 20128.

Código fuente de OmniRoute (a58000c7685f)

HagiCode

HagiCode es un espacio de trabajo de programación con agentes, flujos estructurados, ejecución multiagente y vistas de Hero Dungeon.

Convierte ideas en software útil con un flujo de trabajo con agentes más inteligente, rápido y ameno.

Interfaz principal de HagiCode con tema claro
  • SmartLos flujos estructurados convierten la intención en un itinerario ejecutable desde la idea hasta la entrega.
  • EfficientLos flujos multiagente permiten avanzar en paralelo con la investigación, implementación y revisión.
  • FunHero Dungeon hace que las largas sesiones de programación sean visuales y colaborativas.
Visitar HagiCode