# 🐳 Docker Guide — OmniRoute (Français) 🌐 **Languages:** đŸ‡ș🇾 [English](../../../../guides/DOCKER_GUIDE.md) · đŸ‡ȘđŸ‡č [am](../../../am/docs/guides/DOCKER_GUIDE.md) · 🇾🇩 [ar](../../../ar/docs/guides/DOCKER_GUIDE.md) · 🇩🇿 [az](../../../az/docs/guides/DOCKER_GUIDE.md) · 🇧🇬 [bg](../../../bg/docs/guides/DOCKER_GUIDE.md) · đŸ‡§đŸ‡© [bn](../../../bn/docs/guides/DOCKER_GUIDE.md) · 🇧🇩 [bs](../../../bs/docs/guides/DOCKER_GUIDE.md) · 🇹🇿 [cs](../../../cs/docs/guides/DOCKER_GUIDE.md) · đŸ‡©đŸ‡° [da](../../../da/docs/guides/DOCKER_GUIDE.md) · đŸ‡©đŸ‡Ș [de](../../../de/docs/guides/DOCKER_GUIDE.md) · đŸ‡ŹđŸ‡· [el](../../../el/docs/guides/DOCKER_GUIDE.md) · đŸ‡Ș🇾 [es](../../../es/docs/guides/DOCKER_GUIDE.md) · đŸ‡ȘđŸ‡Ș [et](../../../et/docs/guides/DOCKER_GUIDE.md) · đŸ‡źđŸ‡· [fa](../../../fa/docs/guides/DOCKER_GUIDE.md) · đŸ‡«đŸ‡ź [fi](../../../fi/docs/guides/DOCKER_GUIDE.md) · 🇼đŸ‡Ș [ga](../../../ga/docs/guides/DOCKER_GUIDE.md) · 🇼🇳 [gu](../../../gu/docs/guides/DOCKER_GUIDE.md) · 🇳🇬 [ha](../../../ha/docs/guides/DOCKER_GUIDE.md) · đŸ‡źđŸ‡± [he](../../../he/docs/guides/DOCKER_GUIDE.md) · 🇼🇳 [hi](../../../hi/docs/guides/DOCKER_GUIDE.md) · đŸ‡­đŸ‡· [hr](../../../hr/docs/guides/DOCKER_GUIDE.md) · 🇭đŸ‡ș [hu](../../../hu/docs/guides/DOCKER_GUIDE.md) · 🇩đŸ‡Č [hy](../../../hy/docs/guides/DOCKER_GUIDE.md) · đŸ‡źđŸ‡© [id](../../../id/docs/guides/DOCKER_GUIDE.md) · 🇳🇬 [ig](../../../ig/docs/guides/DOCKER_GUIDE.md) · 🇼đŸ‡č [it](../../../it/docs/guides/DOCKER_GUIDE.md) · đŸ‡ŻđŸ‡” [ja](../../../ja/docs/guides/DOCKER_GUIDE.md) · 🇬đŸ‡Ș [ka](../../../ka/docs/guides/DOCKER_GUIDE.md) · 🇰🇭 [km](../../../km/docs/guides/DOCKER_GUIDE.md) · 🇼🇳 [kn](../../../kn/docs/guides/DOCKER_GUIDE.md) · đŸ‡°đŸ‡· [ko](../../../ko/docs/guides/DOCKER_GUIDE.md) · đŸ‡±đŸ‡č [lt](../../../lt/docs/guides/DOCKER_GUIDE.md) · đŸ‡±đŸ‡» [lv](../../../lv/docs/guides/DOCKER_GUIDE.md) · 🇼🇳 [ml](../../../ml/docs/guides/DOCKER_GUIDE.md) · 🇼🇳 [mr](../../../mr/docs/guides/DOCKER_GUIDE.md) · đŸ‡ČđŸ‡Ÿ [ms](../../../ms/docs/guides/DOCKER_GUIDE.md) · đŸ‡ČđŸ‡č [mt](../../../mt/docs/guides/DOCKER_GUIDE.md) · đŸ‡ČđŸ‡Č [my](../../../my/docs/guides/DOCKER_GUIDE.md) · đŸ‡łđŸ‡” [ne](../../../ne/docs/guides/DOCKER_GUIDE.md) · đŸ‡łđŸ‡± [nl](../../../nl/docs/guides/DOCKER_GUIDE.md) · 🇳🇮 [no](../../../no/docs/guides/DOCKER_GUIDE.md) · 🇼🇳 [or](../../../or/docs/guides/DOCKER_GUIDE.md) · 🇼🇳 [pa](../../../pa/docs/guides/DOCKER_GUIDE.md) · đŸ‡”đŸ‡­ [phi](../../../phi/docs/guides/DOCKER_GUIDE.md) · đŸ‡”đŸ‡± [pl](../../../pl/docs/guides/DOCKER_GUIDE.md) · đŸ‡”đŸ‡č [pt](../../../pt/docs/guides/DOCKER_GUIDE.md) · đŸ‡§đŸ‡· [pt-BR](../../../pt-BR/docs/guides/DOCKER_GUIDE.md) · đŸ‡·đŸ‡Ž [ro](../../../ro/docs/guides/DOCKER_GUIDE.md) · đŸ‡·đŸ‡ș [ru](../../../ru/docs/guides/DOCKER_GUIDE.md) · đŸ‡±đŸ‡° [si](../../../si/docs/guides/DOCKER_GUIDE.md) · 🇾🇰 [sk](../../../sk/docs/guides/DOCKER_GUIDE.md) · 🇾🇼 [sl](../../../sl/docs/guides/DOCKER_GUIDE.md) · đŸ‡·đŸ‡ž [sr](../../../sr/docs/guides/DOCKER_GUIDE.md) · 🇾đŸ‡Ș [sv](../../../sv/docs/guides/DOCKER_GUIDE.md) · 🇰đŸ‡Ș [sw](../../../sw/docs/guides/DOCKER_GUIDE.md) · 🇼🇳 [ta](../../../ta/docs/guides/DOCKER_GUIDE.md) · 🇼🇳 [te](../../../te/docs/guides/DOCKER_GUIDE.md) · đŸ‡č🇭 [th](../../../th/docs/guides/DOCKER_GUIDE.md) · đŸ‡čđŸ‡· [tr](../../../tr/docs/guides/DOCKER_GUIDE.md) · đŸ‡ș🇩 [uk-UA](../../../uk-UA/docs/guides/DOCKER_GUIDE.md) · đŸ‡”đŸ‡° [ur](../../../ur/docs/guides/DOCKER_GUIDE.md) · đŸ‡ș🇿 [uz](../../../uz/docs/guides/DOCKER_GUIDE.md) · đŸ‡»đŸ‡ł [vi](../../../vi/docs/guides/DOCKER_GUIDE.md) · 🇳🇬 [yo](../../../yo/docs/guides/DOCKER_GUIDE.md) · 🇹🇳 [zh-CN](../../../zh-CN/docs/guides/DOCKER_GUIDE.md) · đŸ‡čđŸ‡Œ [zh-TW](../../../zh-TW/docs/guides/DOCKER_GUIDE.md) --- > RĂ©fĂ©rence complĂšte du dĂ©ploiement avec Docker. Pour dĂ©marrer rapidement, consultez la [section Docker du README](../README.md#-docker). ## Table des matiĂšres - [ExĂ©cution rapide](#quick-run) - [Avec un fichier d’environnement](#with-environment-file) - [Docker Compose](#docker-compose) - [Profils disponibles](#available-profiles) - [Configuration des outils CLI de l’hĂŽte lorsqu’OmniRoute s’exĂ©cute dans Docker](#configuring-host-cli-tools-when-omniroute-runs-in-docker) - [Conteneur compagnon Redis](#redis-sidecar) - [Configuration Compose pour la production](#production-compose) - [Étapes du Dockerfile](#dockerfile-stages) - [Variables d’environnement critiques](#critical-environment-variables) - [Docker Compose avec Caddy (HTTPS)](#docker-compose-with-caddy-https-auto-tls) - [Tunnel rapide Cloudflare](#cloudflare-quick-tunnel) - [Étiquettes d’image](#image-tags) - [Mises Ă  jour du tableau de bord dans Docker](#dashboard-updates-in-docker) - [DisponibilitĂ© : SQLite par dĂ©faut ne prend en charge qu’un seul rĂ©plica](#availability-default-sqlite-is-single-replica) - [Erreurs rĂ©gionales de Gemini dans Docker](#gemini-regional-errors-inside-docker) - [Remarques importantes](#important-notes) --- ## ExĂ©cution rapide > **Vous souhaitez auto-hĂ©berger avec une seule commande ?** Consultez le > [guide d’auto-hĂ©bergement](../getting-started/SELF_HOST_GUIDE.md) — > `docker compose -f docker-compose.selfhost.yml up -d` (image publiĂ©e + > Redis, accĂšs limitĂ© Ă  l’interface de bouclage, aucun choix de profil). L’exĂ©cution rapide ci-dessous correspond Ă  > l’approche Ă  conteneur unique pour les utilisateurs qui exĂ©cutent dĂ©jĂ  Redis ailleurs. ```bash docker run -d \ --name omniroute \ --restart unless-stopped \ --stop-timeout 40 \ -p 20128:20128 \ -v omniroute-data:/app/data \ diegosouzapw/omniroute:latest ``` ## Avec un fichier d’environnement ```bash # Commencez par copier et modifier .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 ``` ## Docker Compose ```bash # Profil de base (sans outils CLI) docker compose --profile base up -d # Profil CLI (Claude Code, Codex et OpenClaw intĂ©grĂ©s) docker compose --profile cli up -d # Profil hĂŽte (principalement pour Linux ; monte les exĂ©cutables CLI de l’hĂŽte en lecture seule) docker compose --profile host up -d # Profil web (Chromium/Playwright pour les fournisseurs utilisant des sessions web) docker compose --profile web up -d # Combiner CLI avec le conteneur compagnon CLIProxyAPI docker compose --profile cli --profile cliproxyapi up -d ``` ## Profils disponibles OmniRoute fournit des profils Compose adaptĂ©s aux principales configurations de dĂ©ploiement. Choisissez celui qui correspond Ă  votre environnement. | Profil | Service | Cas d’utilisation | Commande | | --------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | | `base` (dĂ©faut) | `omniroute-base` | Serveur sans interface graphique / environnement d’exĂ©cution minimal, sans CLI de fournisseurs incluses | `docker compose --profile base up -d` | | `cli` | `omniroute-cli` | Flux de travail agentiques appelant `omniroute providers/setup/doctor` et les CLI incluses (Codex, Claude Code, Droid, OpenClaw) | `docker compose --profile cli up -d` | | `host` | `omniroute-host` | HĂŽtes Linux souhaitant un accĂšs de type `network_mode` aux CLI de l’hĂŽte en montant `~/.local/bin`, `~/.codex`, `~/.claude`, etc. en lecture seule | `docker compose --profile host up -d` | | `cliproxyapi` | `cliproxyapi` | ExĂ©cuter le conteneur compagnon [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI) sur le port `8317` afin de relayer les CLI en amont | `docker compose --profile cliproxyapi up -d` | | `web` | `omniroute-web` | Fournisseurs utilisant des sessions web et nĂ©cessitant un navigateur : `gemini-web`, `claude-web`, `claude-turnstile` (construit `runner-web`, Chromium inclus) | `docker compose --profile web up -d` | > Plusieurs profils peuvent ĂȘtre combinĂ©s : `docker compose --profile cli --profile cliproxyapi up -d`. ## Configuration des outils CLI de l’hĂŽte lorsque OmniRoute s’exĂ©cute dans Docker `omniroute setup-codex`, `setup-claude`, `config set ` et le bouton **Enregistrer la configuration** du tableau de bord Ă©crivent tous des fichiers tels que `~/.codex/*.config.toml`. Ces chemins n’ont de sens que sur la machine oĂč la CLI s’exĂ©cute rĂ©ellement. Si vous les exĂ©cutez dans le conteneur, l’écriture s’effectue dans le rĂ©pertoire personnel du conteneur (`/home/node` — l’image s’exĂ©cute avec `USER node`), qu’aucune CLI de l’hĂŽte ne consultera jamais et dont le contenu est supprimĂ© dĂšs que le conteneur est recréé. OmniRoute dĂ©tecte cette situation et refuse l’écriture en fournissant des instructions au lieu de signaler une rĂ©ussite dont vous ne pourriez pas profiter : la CLI se termine avec le code `2`, et l’API rĂ©pond `422` avec `containerEphemeralTarget: true`. ### Recommandation : exĂ©cuter la CLI sur l’hĂŽte et OmniRoute dans Docker Le conteneur fournit l’API ; la CLI configure les outils de votre hĂŽte. ```bash docker compose --profile base up -d npm install -g omniroute omniroute connect http://localhost:20128 # dirige la CLI vers le conteneur omniroute setup-codex # Ă©crit dans le vĂ©ritable ~/.codex sur votre hĂŽte ``` C’est le bon choix lorsque Codex, Claude Code, Cursor ou des outils similaires s’exĂ©cutent sur votre ordinateur portable — ce qui constitue la configuration habituelle. ### Alternative : monter les rĂ©pertoires de configuration de l’hĂŽte avec bind mount (profil `host`) Si vous souhaitez que le conteneur lui-mĂȘme Ă©crive dans la configuration de votre hĂŽte, montez les rĂ©pertoires et faites pointer `CLI_CONFIG_HOME` vers la racine du montage. Le profil `host` le fait dĂ©jĂ  : ```yaml environment: - CLI_CONFIG_HOME=/host-home - CLI_ALLOW_CONFIG_WRITES=true volumes: - ~/.codex:/host-home/.codex:rw - ~/.claude:/host-home/.claude:rw ``` Un bind mount est ce qui rend le chemin fiable : OmniRoute lit `/proc/self/mountinfo` et autorise les Ă©critures dans les chemins montĂ©s (ainsi que dans les rĂ©pertoires dont les enfants sont des montages, ce qui correspond exactement Ă  la structure `/host-home` ci-dessus), tout en continuant Ă  refuser les chemins non montĂ©s. ### Échappatoire : configurer les propres CLI du conteneur (Ă  utiliser avec parcimonie) Lorsque les CLI se trouvent rĂ©ellement dans le conteneur (profil `cli`), l’écriture est intentionnelle. Transmettez `--allow-container-write` Ă  toute commande `setup-*`, ou dĂ©finissez `OMNIROUTE_ALLOW_CONTAINER_CONFIG_WRITE=true` pour le serveur. L’écriture s’effectue avec un avertissement indiquant qu’elle ne survivra pas au conteneur. > **Avertissement de sĂ©curitĂ© — profil `cli` + montage de `docker.sock`.** > Le profil `cli` monte `/var/run/docker.sock` avec bind mount afin que le programme de mise Ă  jour automatique > exĂ©cutĂ© dans le conteneur puisse recrĂ©er la pile depuis le dĂ©mon de l’hĂŽte > (`src/lib/system/autoUpdate.ts` recherche ce socket et ignore le > chemin Docker lorsqu’il est absent). Ce socket constitue **une frontiĂšre de confiance donnant un accĂšs root Ă  l’hĂŽte** : > tout ce qui peut y accĂ©der contrĂŽle le dĂ©mon Docker de l’hĂŽte en tant que > root — et peut crĂ©er, inspecter, arrĂȘter et supprimer n’importe quel conteneur sur l’hĂŽte. > ConsĂ©quences : > > 1. **N’exposez jamais le port du profil `cli` au rĂ©seau.** Publiez-le > sur `127.0.0.1` (`ports: "127.0.0.1:${DASHBOARD_PORT:-20128}:..."`) > — un profil `cli` accessible depuis le rĂ©seau local transforme toute RCE au niveau du tableau de bord en > compromission complĂšte de l’hĂŽte. > 2. **Ne montez aucun rĂ©pertoire supplĂ©mentaire de l’hĂŽte dans le profil `cli`.** > Le socket Docker associĂ© Ă  tout montage supplĂ©mentaire donne au conteneur un accĂšs complet en > lecture/Ă©criture Ă  votre systĂšme de fichiers et Ă  la configuration de l’hĂŽte. Si un outil doit > accĂ©der Ă  un projet, exĂ©cutez-le localement avec le binaire CLI — ne le montez pas > dans le conteneur `cli`. > > Si vous n’avez pas besoin de la mise Ă  jour automatique depuis le conteneur, laissez le profil `cli` dĂ©sactivĂ© > (`COMPOSE_PROFILES=core,redis` ou une valeur plus courte). Les autres profils ne > montent pas le socket Docker. > > Consultez `docs/security/MITM-TPROXY-DECRYPT.md` (git ; non compilĂ© dans `/docs`) pour le modĂšle de menace associĂ© > concernant les attaques MITM, ainsi que `docs/security/SUPPLY_CHAIN.md` pour la > chaĂźne de provenance des binaires `codex`/`claude-code`/`droid`/`openclaw`. ## Sidecar Redis OmniRoute s’appuie sur Redis pour prendre en charge le limiteur de dĂ©bit distribuĂ© et le cache partagĂ©. Le service `redis` est **toujours dĂ©fini** dans `docker-compose.yml` (il n’est conditionnĂ© par aucun profil) et dĂ©marre avec n’importe quel autre profil. | DĂ©tail | Valeur | | ----------------------------- | --------------------------------------------------- | | Image | `redis:7-alpine` | | Nom du conteneur | `omniroute-redis` | | Port interne | `6379` | | Port de l’hĂŽte (modifiable) | `REDIS_PORT` (valeur par dĂ©faut : `6379`) | | Liaison Ă  l’hĂŽte (modifiable) | `REDIS_BIND_HOST` (valeur par dĂ©faut : `127.0.0.1`) | | Volume | `omniroute-redis-data` → `/data` | | VĂ©rification d’état | `redis-cli ping` (intervalle de 10 s) | Variables d’environnement associĂ©es : - `REDIS_URL` — chaĂźne de connexion injectĂ©e dans l’application (`redis://redis:6379` par dĂ©faut). - `REDIS_PORT` — mappage du port cĂŽtĂ© hĂŽte pour le conteneur Redis. - `REDIS_BIND_HOST` — interface de l’hĂŽte sur laquelle le port est publiĂ©. La valeur par dĂ©faut est `127.0.0.1`. > **Pourquoi l’interface de bouclage par dĂ©faut :** le sidecar s’exĂ©cute sans `requirepass`, et les conteneurs > de l’application y accĂšdent via le rĂ©seau Compose (`redis:6379`) — le port publiĂ© sert > uniquement aux outils exĂ©cutĂ©s cĂŽtĂ© hĂŽte (`redis-cli`, un `npm run dev` local). Une publication sur > `0.0.0.0` exposerait une instance Redis non authentifiĂ©e Ă  tous les hĂŽtes de votre rĂ©seau local. Si vous dĂ©finissez > `REDIS_BIND_HOST=0.0.0.0`, ajoutez Ă©galement `--requirepass` au champ `command:` du service. Il n’est pas recommandĂ© de **dĂ©sactiver Redis** (le limiteur de dĂ©bit basculera vers une solution de secours en mĂ©moire). Si nĂ©cessaire, supprimez/commentez le bloc du service `redis:` dans `docker-compose.yml` ou rĂ©duisez son nombre d’instances Ă  zĂ©ro : ```bash docker compose up -d --scale redis=0 ``` ## Compose de production Pour exĂ©cuter un instantanĂ© de production isolĂ© parallĂšlement Ă  l’environnement de dĂ©veloppement, utilisez `docker-compose.prod.yml`. | DĂ©tail | Valeur | | ---------------------------------- | ----------------------------------------------------------------------------------------------------- | | Fichier | `docker-compose.prod.yml` | | Port du tableau de bord par dĂ©faut | `PROD_DASHBOARD_PORT=20130` (mappĂ© vers le port interne `${DASHBOARD_PORT:-20128}`) | | Port de l’API par dĂ©faut | `PROD_API_PORT=20131` | | Image | `omniroute:prod` (construite Ă  partir de la cible `runner-cli`) | | Conteneur Redis | `omniroute-redis-prod` (`redis:8.6.2`, volume dĂ©diĂ© `redis-prod-data`) | | Volume de donnĂ©es | `omniroute-prod-data` (nommĂ©, conservĂ© entre les reconstructions) | | VĂ©rifications d’état | `node healthcheck.mjs` + `redis-cli ping`, avec `depends_on` conditionnĂ© par l’état de santĂ© de Redis | Utilisation : ```bash # Construire et dĂ©marrer la pile de production docker compose -f docker-compose.prod.yml up -d --build # Afficher les journaux en continu docker compose -f docker-compose.prod.yml logs -f # ArrĂȘter et supprimer la pile (conserver les volumes) docker compose -f docker-compose.prod.yml down ``` La pile de production s’exĂ©cute en parallĂšle de la configuration Compose de dĂ©veloppement (avec des noms de conteneurs, des ports et des volumes diffĂ©rents). Vous pouvez donc poursuivre vos itĂ©rations locales pendant que la production reste active. ## Étapes du Dockerfile Le dĂ©pĂŽt fournit un Dockerfile multi-Ă©tapes (`Dockerfile`). Quatre Ă©tapes sont exposĂ©es ; choisissez la bonne `target` selon votre cas d’usage. | Étape | Image de base | Objectif | | ------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `builder` | `node:26-trixie-slim` | Installe les dĂ©pendances (`npm ci --legacy-peer-deps`) et exĂ©cute `npm run build` (Turbopack par dĂ©faut — voir Ressources de compilation ci-dessous) | | `runner-base` | `node:26-trixie-slim` | Environnement d’exĂ©cution de production avec la sortie standalone de Next.js. **Aucune CLI de fournisseur incluse.** | | `runner-cli` | `runner-base` | Ajoute `git`, `docker.io`, `docker-compose` et les CLI globales : `@openai/codex`, `@anthropic-ai/claude-code`, `droid`, `openclaw`. **Choisissez cette Ă©tape pour les workflows agentiques.** | | `runner-web` | `runner-base` | Ajoute Playwright et un navigateur Chromium (`--with-deps`) pour les fournisseurs de sessions web : `gemini-web`, `claude-web`, `claude-turnstile`. **Choisissez cette Ă©tape si vous utilisez ces fournisseurs** — l’image standard Ă©choue au moment de la requĂȘte sans ces Ă©lĂ©ments (voir la remarque sur `-web` dans Canaux de publication). | Construisez manuellement une cible spĂ©cifique : ```bash docker build --target runner-base -t omniroute:base . docker build --target runner-cli -t omniroute:cli . docker build --target runner-web -t omniroute:web . ``` ### Ressources de compilation Trois arguments de compilation dĂ©terminent le coĂ»t de l’étape `builder`. Ils s’appliquent uniquement au moment de la compilation — `OMNIROUTE_MEMORY_MB` (ci-dessous) est un paramĂštre distinct pour l’exĂ©cution. | Argument de compilation | Valeur par dĂ©faut | Effet | | --------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------- | | `OMNIROUTE_USE_TURBOPACK` | `0` | `0` compile avec webpack : consommation mĂ©moire maximale plus faible, mais plus lent. `1` active Turbopack. | | `OMNIROUTE_BUILD_MEMORY_MB` | `6144` | Plafond du tas V8 (`--max-old-space-size`) pour le processus `next build` lancĂ©. | | `OMNIROUTE_BUILD_WORKERS` | `2` | Alimente `CIRCLE_NODE_TOTAL` ; Next calcule `workers = N - 1` pour la collecte des donnĂ©es de pages. | `OMNIROUTE_BUILD_WORKERS` est le paramĂštre Ă  augmenter sur une machine de compilation puissante et celui Ă  suspecter lorsqu’une compilation aux ressources limitĂ©es Ă©choue **aprĂšs** `✓ Compiled successfully`. Chaque worker de donnĂ©es de pages constitue son propre processus, tout comme le processus parent `next build` ; une reproduction sur un VPS actif (issue #7518) a mesurĂ© le pic de RSS de chaque processus Ă  ~4,5 Go, indĂ©pendamment de l’option de tas `NODE_OPTIONS` (Turbopack compile dans de la mĂ©moire native/Rust en dehors du tas V8). La valeur par dĂ©faut de `2` (→ 1 worker, 2 processus au total) est dimensionnĂ©e pour les runners hĂ©bergĂ©s par GitHub dotĂ©s de 16 Go / 4 vCPU que le pipeline de publication utilise. Avec `8` (→ 7 workers), ce runner a manquĂ© de mĂ©moire et buildkit a fait Ă©chouer l’étape avec `ResourceExhausted: ... cannot allocate memory` ; `3` (→ 2 workers) ne tenait toujours pas une fois la RSS par processus mesurĂ©e directement plutĂŽt que dĂ©duite. `tests/unit/docker-build-memory-budget.test.ts` effectue le calcul Ă  partir de la valeur mesurĂ©e et Ă©choue si l’un ou l’autre paramĂštre dĂ©passe les capacitĂ©s du runner. Turbopack compile dans de la mĂ©moire Rust native situĂ©e **en dehors** du tas V8, donc `OMNIROUTE_BUILD_MEMORY_MB` ne la limite pas. Sur un hĂŽte soumis Ă  un plafond de mĂ©moire, la compilation est alors interrompue par un SIGKILL de l’OOM killer sans aucun message d’erreur — elle s’arrĂȘte simplement au milieu de `Creating an optimized production build`, ce qui ressemble davantage Ă  un blocage qu’à un manque de mĂ©moire. C’est pourquoi le `Dockerfile` utilise webpack par dĂ©faut (`OMNIROUTE_USE_TURBOPACK=0`), contrairement Ă  `npm run dev` / `npm run build`, oĂč Turbopack est la valeur par dĂ©faut dans le code : une simple commande `docker build .` sans argument de compilation (telle que Railway et d’autres hĂ©bergeurs en un clic l’exĂ©cutent) ne doit pas Ă©chouer silencieusement sur une machine de compilation Ă  mĂ©moire limitĂ©e. Les images publiĂ©es transmettent dĂ©jĂ  explicitement `OMNIROUTE_USE_TURBOPACK=0` dans `docker-publish.yml`. Sur une machine de compilation disposant de beaucoup de RAM, activez Turbopack pour accĂ©lĂ©rer la compilation : ```bash docker build --target runner-base \ --build-arg OMNIROUTE_USE_TURBOPACK=1 \ -t omniroute:base . ``` `webpackBuildWorker` est activĂ©, donc `next build` exĂ©cute un processus parent **et** un processus worker, et chacun respecte `OMNIROUTE_BUILD_MEMORY_MB` sĂ©parĂ©ment. Dimensionnez le plafond du conteneur Ă  environ deux fois cette valeur, et non une seule fois. Mesures effectuĂ©es sur cette arborescence (`--target runner-base`, `OMNIROUTE_BUILD_MEMORY_MB=6144`) : | Bundler | Plafond du conteneur | RĂ©sultat | | --------- | -------------------- | -------------------------------------------------------------- | | Turbopack | 8 Gio / 16 Gio | interrompu par l’OOM killer dans les deux cas, silencieusement | | webpack | 8 Gio | worker de compilation interrompu par SIGKILL | | webpack | 12 Gio | rĂ©ussite, avec un pic Ă  11,1 Gio | ### Valeurs par dĂ©faut Ă  l’exĂ©cution Valeurs par dĂ©faut exportĂ©es par `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`. Comportement de la mĂ©moire dans Docker : - L’image dĂ©finit `OMNIROUTE_MEMORY_MB=1024` et en dĂ©duit `NODE_OPTIONS=--max-old-space-size=1024`. - Le processus serveur rĂ©el est dĂ©marrĂ© par le lanceur autonome, qui lit `OMNIROUTE_MEMORY_MB` et ajoute `--max-old-space-size=`. - Node utilise la derniĂšre valeur rĂ©pĂ©tĂ©e de `--max-old-space-size`. Ainsi, la dĂ©finition de `OMNIROUTE_MEMORY_MB` contrĂŽle la limite effective du tas sous Docker. - Comme l’image la dĂ©finit systĂ©matiquement, la valeur de secours du lanceur, calibrĂ©e en fonction de la RAM, ne s’applique jamais sous Docker. Augmentez-la explicitement selon la charge de travail (voir le tableau ci-dessous). `2048` reste insuffisant pour `/v1/responses` avec des agents de codage. ### RAM d’exĂ©cution pour les agents de codage La valeur Docker par dĂ©faut de 1 Gio constitue un minimum pour le tableau de bord et les conversations lĂ©gĂšres, et non un dimensionnement adaptĂ© Ă  la production. Les corps volumineux de requĂȘtes `POST /v1/responses` (des centaines de messages et des dizaines d’outils) conservent plusieurs graphes en mĂ©moire pendant la compression. Deux requĂȘtes simultanĂ©es d’environ 3 Mio / 750 000 jetons ont provoquĂ© l’arrĂȘt de V8 avec un espace ancien de **12 Gio** (`FATAL ERROR: Reached heap limit`) et ont Ă©galement dĂ©clenchĂ© une erreur de mĂ©moire insuffisante du cgroup Ă  16 Gio. Voir [#7849](https://github.com/diegosouzapw/OmniRoute/issues/7849). Dimensionnez la **mĂ©moire cgroup `--memory` au-dessus de la taille du tas** — les tampons natifs, SQLite et les donnĂ©es intermĂ©diaires de compression rĂ©sident en dehors de V8. | Charge de travail | `OMNIROUTE_MEMORY_MB` | Conteneur / cgroup | Remarques | | ------------------------------------------------- | ------------------------------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | | Tableau de bord, une conversation lĂ©gĂšre | `1024` (valeur par dĂ©faut de l’image) | ≄2 Gio | | | Un agent de codage (Claude/Codex/Grok) | `8192` | ≄10 Gio | Session unique `/v1/responses` typique | | Deux longues requĂȘtes `/v1/responses` simultanĂ©es | `10240`–`12288` | ≄12–16 Gio | ArrĂȘt de V8 mesurĂ© avec un tas d’environ 12 Gio | | Trois contextes longs simultanĂ©s ou plus | ne pas utiliser un seul processus | sĂ©rialiser / ajouter de la RAM | Par dĂ©faut, une seule requĂȘte lourde est admise simultanĂ©ment ; augmenter cette limite sans ajouter de RAM provoque de nouveau l’arrĂȘt | Sur une machine physique, `omniroute serve` calibre la limite Ă  environ 35 % de la RAM (bornĂ©e Ă  `[512, 4096]`) lorsque `OMNIROUTE_MEMORY_MB` n’est **pas dĂ©finie**. Docker dĂ©finit toujours la valeur sur `1024` ; ce calibrage ne s’exĂ©cute donc jamais dans l’image officielle. ```bash 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 ``` ## Variables d’environnement critiques Au-delĂ  des valeurs par dĂ©faut documentĂ©es dans [ENVIRONMENT.md](../reference/ENVIRONMENT.md), les variables suivantes sont les plus importantes lors de l’exĂ©cution sous Docker : | Variable | RĂŽle | Valeur par dĂ©faut | | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | | `OMNIROUTE_WS_BRIDGE_SECRET` | Secret partagĂ© pour le pont WebSocket. **Obligatoire en production** — dĂ©finissez une chaĂźne alĂ©atoire robuste. | non dĂ©finie (Ă  fournir) | | `REDIS_URL` | ChaĂźne de connexion pour le backend du limiteur de dĂ©bit / cache | `redis://redis:6379` | | `REDIS_PORT` | Port cĂŽtĂ© hĂŽte pour le conteneur Redis inclus | `6379` | | `REDIS_BIND_HOST` | Interface hĂŽte sur laquelle le port Redis inclus est publiĂ© (boucle locale, sauf si vous ajoutez AUTH) | `127.0.0.1` | | `AUTO_UPDATE_HOST_REPO_DIR` | Chemin hĂŽte montĂ© dans le profil `cli` Ă  l’emplacement `/workspace/omniroute` pour les processus de mise Ă  jour automatique | `.` (rĂ©pertoire courant) | | `OMNIROUTE_MEMORY_MB` | Limite du tas Node Ă  l’exĂ©cution pour le serveur Docker autonome ; remplace la valeur par dĂ©faut de l’image indiquĂ©e ci-dessus. Agents de codage : `8192`+ (voir [RAM d’exĂ©cution](#runtime-ram-for-coding-agents)). | `1024` | | `DASHBOARD_PORT` / `API_PORT` | Remplace les ports exposĂ©s du tableau de bord (20128) et de l’API (20129) | `20128` / `20129` | | `APP_BIND_HOST` | Interface hĂŽte sur laquelle docker-compose publie les ports du tableau de bord, de l’API et du WS en direct. Avec `REQUIRE_API_KEY=false` (valeur par dĂ©faut), `0.0.0.0` expose le proxy `/v1` anonyme au rĂ©seau local — n’élargissez l’accĂšs qu’avec `REQUIRE_API_KEY=true` ou un proxy inverse en amont. | `127.0.0.1` | | `CLIPROXY_BIND_HOST` | Interface hĂŽte sur laquelle docker-compose publie le conteneur auxiliaire `cliproxyapi` — son volume de donnĂ©es contient les identifiants des fournisseurs. | `127.0.0.1` | | `OMNIROUTE_PLUGINS_DIR` | RĂ©pertoire lu par l’analyseur de plugins Ă  l’exĂ©cution et dans lequel il effectue les installations. DĂ©finissez-le lorsque les plugins sont montĂ©s par liaison : la valeur par dĂ©faut dĂ©pend de `HOME`, qu’une image n’exporte pas nĂ©cessairement. | `~/.omniroute/plugins` | | `OMNIROUTE_BASE_PATH` | Sous-chemin d’URL lorsque l’application est publiĂ©e derriĂšre un proxy inverse (par ex. `/omniroute`) | _(vide = racine)_ | | `NEXT_PUBLIC_BASE_URL` | Origine publique du navigateur incluant le sous-chemin (par ex. `https://host/omniroute`) | non dĂ©finie | | `PROD_DASHBOARD_PORT` | Port du tableau de bord cĂŽtĂ© hĂŽte pour `docker-compose.prod.yml` | `20130` | | `CLIPROXYAPI_PORT` | Port cĂŽtĂ© hĂŽte pour le conteneur auxiliaire `cliproxyapi` | `8317` | ## Proxy inverse sur un sous-chemin (Traefik / nginx) Le `basePath` de Next.js est compilĂ© dans le bundle autonome. OmniRoute enregistre la valeur intĂ©grĂ©e dans un fichier sentinelle Ă  la racine de l’application (Ă©crit pendant `npm run build` et lu par `scripts/docker/ensure-docker-base-path.mjs`) et la compare Ă  `OMNIROUTE_BASE_PATH` au dĂ©marrage du conteneur. Lorsqu’elles diffĂšrent et que l’image a Ă©tĂ© construite pour la racine du domaine, le point d’entrĂ©e réécrit les manifestes autonomes, les littĂ©raux `basePath`/`assetPrefix` intĂ©grĂ©s (Next 16 gĂ©nĂšre les URL des ressources SSR uniquement Ă  partir de `assetPrefix` — l’outil de correction y rĂ©plique le sous-chemin), les URL de ressources `/_next/static` intĂ©grĂ©es (manifestes de rĂ©fĂ©rences client, imports de mĂ©dias, pages d’erreur prĂ©rendues) ainsi que la couche de compatibilitĂ© `process.env` cĂŽtĂ© client avant l’exĂ©cution de `node dev/run-standalone.mjs`. ### Construction avec Compose (recommandĂ©e) DĂ©finissez les deux variables dans `.env`, puis reconstruisez afin que l’image et l’environnement d’exĂ©cution concordent : ```bash # .env OMNIROUTE_BASE_PATH=/omniroute NEXT_PUBLIC_BASE_URL=https://myhostname.example.com/omniroute ``` ```bash docker compose --profile base up -d --build ``` `docker-compose.yml` transmet `OMNIROUTE_BASE_PATH` comme argument de construction Docker et comme variable d’environnement d’exĂ©cution. ### Image racine prĂ©construite + sous-chemin Ă  l’exĂ©cution Les images publiĂ©es `diegosouzapw/omniroute:*` sont construites pour la racine du domaine. Vous pouvez nĂ©anmoins dĂ©finir `OMNIROUTE_BASE_PATH` Ă  l’exĂ©cution ; le conteneur corrige le bundle une fois au dĂ©marrage. Associez-le Ă  l’origine publique correspondante : ```yaml services: omniroute: image: diegosouzapw/omniroute:latest environment: OMNIROUTE_BASE_PATH: /omniroute NEXT_PUBLIC_BASE_URL: https://myhostname.example.com/omniroute ``` Configurez le proxy inverse pour transfĂ©rer le chemin externe **complet** (ne supprimez pas le prĂ©fixe). Traefik doit acheminer `PathPrefix(`/omniroute`)` vers le conteneur sans `StripPrefix`, afin que Next.js reçoive `/omniroute/...` et serve les ressources depuis `/omniroute/_next/...`. Le contrĂŽle d’intĂ©gritĂ© Docker interroge le point de terminaison lĂ©ger de cycle de vie `/healthz`, prĂ©fixĂ© par la valeur active de `OMNIROUTE_BASE_PATH`. `/api/monitoring/health` reste disponible pour les diagnostics humains ou ceux des tableaux de bord ; pour que le HEALTHCHECK du conteneur l’utilise de nouveau (par exemple afin d’imposer un contrĂŽle d’intĂ©gritĂ© approfondi), dĂ©finissez `OMNIROUTE_HEALTHCHECK_PATH=/api/monitoring/health`. Ce chemin effectue un contrĂŽle **approfondi** (base de donnĂ©es + synthĂšse de la surveillance) — il convient au `HEALTHCHECK` peu frĂ©quent de Docker si vous choisissez de le rĂ©activer, mais **pas** aux intervalles de `livenessProbe` de Kubernetes. Pour les orchestrateurs (Kubernetes, Nomad, etc.) : | Sonde | À privilĂ©gier | À Ă©viter | | ------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | ActivitĂ© | HTTP `GET /livez`, ou TCP sur le port principal (`PORT`, valeur par dĂ©faut `20128`) | `/api/monitoring/health` comme sonde d’activitĂ© | | DisponibilitĂ© | HTTP `GET /healthz` | Les dĂ©lais courts qui considĂšrent une boucle d’évĂ©nements occupĂ©e comme morte | | Approfondie / boĂźte noire | `/api/monitoring/health` | — | `/healthz` indique l’état du cycle de vie du processus (`ok` / `starting` / `stopping`). `/livez` vĂ©rifie uniquement que le processus est actif (200 chaque fois que le gestionnaire peut s’exĂ©cuter ; il n’attend pas que le service soit prĂȘt). Les deux s’exĂ©cutent toujours sur la mĂȘme boucle d’évĂ©nements Node que le traitement des requĂȘtes ; les opĂ©rations de catalogue ou de compression qui sollicitent fortement le processeur peuvent donc les retarder — occupĂ© ≠ mort. PrivilĂ©giez une sonde d’activitĂ© TCP si les sondes HTTP expirent. Guide complet sur les sondes : [Guide de surveillance — recommandations relatives aux sondes Kubernetes](../ops/MONITORING_GUIDE.md#kubernetes-probe-recommendations). ## Docker Compose avec Caddy (HTTPS Auto-TLS) OmniRoute peut ĂȘtre exposĂ© de maniĂšre sĂ©curisĂ©e grĂące au provisionnement SSL automatique de Caddy. Assurez-vous que l’enregistrement DNS A de votre domaine pointe vers l’adresse IP de votre serveur. ```yaml services: omniroute: image: diegosouzapw/omniroute:latest container_name: omniroute restart: unless-stopped volumes: - omniroute-data:/app/data environment: - PORT=20128 # Origine visible par le navigateur pour les rappels OAuth, les liens du tableau de bord et les URL publiques gĂ©nĂ©rĂ©es. - NEXT_PUBLIC_BASE_URL=https://your-domain.com # URL interne de serveur Ă  serveur pour les tĂąches planifiĂ©es et les auto-requĂȘtes. - 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 dĂ©finit les en-tĂȘtes de transfert standard pour le conteneur en amont. OmniRoute utilise `NEXT_PUBLIC_BASE_URL` comme origine publique canonique pour les rappels OAuth et les liens publics gĂ©nĂ©rĂ©s ; les Ă©critures authentifiĂ©es du tableau de bord utilisent des requĂȘtes de mĂȘme origine ainsi qu’une protection CSRF liĂ©e Ă  la session. N’activez `OMNIROUTE_TRUST_PROXY` que pour les dĂ©ploiements avancĂ©s dans lesquels vous souhaitez dĂ©libĂ©rĂ©ment qu’OmniRoute dĂ©duise l’origine publique Ă  partir d’en-tĂȘtes transfĂ©rĂ©s approuvĂ©s plutĂŽt que d’une configuration explicite. ## Tunnel rapide Cloudflare La prise en charge du tableau de bord pour les dĂ©ploiements Docker comprend un **tunnel rapide Cloudflare** activable en un clic dans `Dashboard → Endpoints`. Lors de la premiĂšre activation, `cloudflared` est tĂ©lĂ©chargĂ© uniquement si nĂ©cessaire, un tunnel temporaire vers votre point de terminaison `/v1` actuel est dĂ©marrĂ©, et l’URL `https://*.trycloudflare.com/v1` gĂ©nĂ©rĂ©e s’affiche directement sous votre URL publique habituelle. Les panneaux de tunnel des points de terminaison (Cloudflare, Tailscale, ngrok) peuvent ĂȘtre affichĂ©s ou masquĂ©s depuis `Settings → Appearance` sans modifier l’état des tunnels actifs. ### Remarques sur les tunnels - Les URL des tunnels rapides sont temporaires et changent aprĂšs chaque redĂ©marrage. - Les tunnels rapides ne sont pas restaurĂ©s automatiquement aprĂšs le redĂ©marrage d’OmniRoute ou du conteneur. RĂ©activez-les depuis le tableau de bord lorsque cela est nĂ©cessaire. - L’installation gĂ©rĂ©e prend actuellement en charge Linux, macOS et Windows sur `x64` / `arm64`. - Les tunnels rapides gĂ©rĂ©s utilisent par dĂ©faut le transport HTTP/2 afin d’éviter les avertissements bruyants concernant les tampons UDP de QUIC dans les environnements de conteneurs contraints. DĂ©finissez `CLOUDFLARED_PROTOCOL=quic` ou `auto` si vous souhaitez utiliser un autre transport. - Les images Docker incluent les certificats racines des autoritĂ©s de certification du systĂšme et les transmettent Ă  l’instance gĂ©rĂ©e de `cloudflared`, ce qui Ă©vite les Ă©checs d’approbation TLS lorsque le tunnel s’initialise dans le conteneur. - DĂ©finissez `CLOUDFLARED_BIN=/absolute/path/to/cloudflared` si vous souhaitez qu’OmniRoute utilise un binaire existant au lieu d’en tĂ©lĂ©charger un. ## Étiquettes d’image | Image | Étiquette | Taille | Description | | ------------------------ | --------- | ------ | ----------------------------------------------------------------- | | `diegosouzapw/omniroute` | `latest` | ~250MB | Version SemVer stable **publiĂ©e** la plus Ă©levĂ©e (pas git `main`) | | `diegosouzapw/omniroute` | `3.8.0` | ~250MB | Épinglez cette catĂ©gorie d’étiquette pour GitOps | Manifeste multiplateforme : `linux/amd64` + `linux/arm64` natif (Apple Silicon, AWS Graviton, Raspberry Pi). Docker sĂ©lectionne automatiquement l’architecture correspondante ; transmettez `--platform linux/amd64` si vous devez forcer l’émulation AMD64 sur des hĂŽtes ARM. ### Canaux de publication OmniRoute publie des canaux Docker distincts pour les versions stables, les tests de la branche de publication active et les versions de dĂ©veloppement. | Canal | Source | MutabilitĂ© | Utilisation recommandĂ©e | | ------------------------------- | ---------------------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | | `:` / `:-web` | Version signĂ©e/versionnĂ©e | Immuable | DĂ©ploiements en production Ă©pinglant une version exacte | | `:latest` / `:latest-web` | SemVer stable **publiĂ©e** la plus Ă©levĂ©e | Pointeur stable modifiable | Suit les versions stables **aprĂšs** une tĂąche de publication SemVer — ne suit **pas** `main` ni les commits `release/v*` non publiĂ©s | | `:next` / `:next-web` | Branche `release/v*` actuelle par dĂ©faut | Pointeur de prĂ©version modifiable | Test des correctifs intĂ©grĂ©s Ă  la branche de publication active, mais pas encore inclus dans une version stable | | `:main` / `:main-web` | Branche `main` | Pointeur de dĂ©veloppement modifiable | DĂ©veloppement et tests d’intĂ©gration uniquement | #### Fournisseurs de sessions web : les images `-web` Chaque canal ci-dessus est Ă©galement disponible sous forme d’étiquette `-web` (`:latest-web`, `:-web`, `:next-web`, `:main-web`), construite Ă  partir de l’étape `runner-web` — la mĂȘme image, avec Playwright et un navigateur Chromium en plus. L’image standard est fournie **sans** Chromium ; `gemini-web`, `claude-web` et `claude-turnstile` en ont besoin. L’échec est diffĂ©rĂ© et ne survient pas au dĂ©marrage : ces fournisseurs rĂ©pertorient leurs modĂšles et apparaissent comme connectĂ©s dans le tableau de bord, et seule la premiĂšre requĂȘte Ă©choue avec ``` [500]: Failed to load external module playwright: Error: Cannot find module '/app/node_modules/playwright/node_modules/playwright-core/browsers.json' ``` Si vous utilisez ces fournisseurs, rĂ©cupĂ©rez l’étiquette `-web` du canal que vous utilisez dĂ©jĂ  — rien d’autre ne change. Pour une installation npm/CLI (sans image Docker), l’élĂ©ment manquant Ă©quivalent est le binaire du navigateur : exĂ©cutez `npx playwright install chromium` sur l’hĂŽte. #### Utilisation du canal de prĂ©version Le canal `next` est reconstruit Ă  chaque push vers la branche `release/v*` par dĂ©faut actuelle et est publiĂ© pour AMD64 et ARM64. Les anciennes branches de maintenance ne peuvent pas l’écraser. Ce canal fournit une image rĂ©cupĂ©rable contenant les correctifs qui ont Ă©tĂ© fusionnĂ©s dans la branche de publication active avant la crĂ©ation du prochain tag stable. ```bash docker pull diegosouzapw/omniroute:next docker pull diegosouzapw/omniroute:next-web ``` Pour Docker Compose, remplacez le tag d’image utilisĂ© par le profil sĂ©lectionnĂ©, puis rĂ©cupĂ©rez l’image et recrĂ©ez le service : ```yaml services: omniroute: image: diegosouzapw/omniroute:next ``` ```bash docker compose pull docker compose up -d ``` #### SĂ©curitĂ© et retour en arriĂšre `next` est un canal de prĂ©version flottant. Il peut changer Ă  chaque push vers la branche de publication active et n’est **pas pris en charge pour une utilisation en production**. Épinglez le condensat de l’image lors de l’évaluation d’un build spĂ©cifique : ```bash docker pull diegosouzapw/omniroute:next docker image inspect diegosouzapw/omniroute:next --format '{{index .RepoDigests 0}}' ``` Avant d’effectuer des tests, sauvegardez le volume de donnĂ©es d’OmniRoute ou le rĂ©pertoire de donnĂ©es montĂ© par liaison. Pour revenir en arriĂšre, restaurez la version stable ou le condensat prĂ©cĂ©demment utilisĂ©, puis recrĂ©ez le conteneur : ```bash docker pull diegosouzapw/omniroute: docker compose up -d ``` Un build de branche de publication ne peut jamais dĂ©placer `latest` ; seule une version sĂ©mantique stable admissible peut promouvoir le pointeur stable. Les images `next` conservent l’inspection de l’image de publication et le contrĂŽle bloquant des vulnĂ©rabilitĂ©s CRITIQUES. **`latest` ne garantit pas l’actualitĂ© par rapport Ă  git.** Les correctifs fusionnĂ©s dans `main` ou dans la branche `release/v*` active ne sont **pas** inclus dans `:latest` tant qu’une image SemVer stable n’a pas Ă©tĂ© publiĂ©e et que la tĂąche de publication n’a pas promu `:latest` (avec le mĂȘme condensat que cette version SemVer). Si `latest` semble figĂ© alors que GitHub affiche dĂ©jĂ  le correctif, rĂ©cupĂ©rez `:next` pour tester la branche de publication ou attendez le tag SemVer. | Votre objectif | Utilisation | | -------------------------------------------------------------------------------- | ---------------------------------------------- | | GitOps / production ne devant subir aucune dĂ©rive | Épingler `:X.Y.Z` (ou le condensat de l’image) | | Suivre les versions stables publiĂ©es et accepter une recrĂ©ation Ă  chaque version | `:latest` | | Tester les commits `release/v*` non publiĂ©s | `:next` (pas en production) | | Tester `main` | `:main` (pas en production) | ## Mises Ă  jour du tableau de bord dans Docker Un conteneur créé Ă  partir d’une image publiĂ©e ne peut pas se remplacer lui-mĂȘme. Lorsqu’OmniRoute dĂ©tecte qu’il s’exĂ©cute dans un conteneur (sans copie de travail `.git` et sans installation npm), la banniĂšre de mise Ă  jour du tableau de bord d’accueil affiche la commande Ă  exĂ©cuter sur l’hĂŽte au lieu des instructions npm/git, avec un bouton de copie : ```bash docker compose pull omniroute && docker compose up -d --force-recreate omniroute ``` Le nom du service provient de `AUTO_UPDATE_SERVICE` (par dĂ©faut `omniroute`, le service dĂ©fini dans `docker-compose.selfhost.yml`). `pull` ne met Ă  jour qu’un tag flottant (`latest`, `next`) ; avec un tag fixe `:X.Y.Z`, modifiez d’abord le tag. ### Mises Ă  jour en un clic avec un fichier dĂ©clencheur sur l’hĂŽte Pour que **Mettre Ă  jour maintenant** continue de fonctionner sans monter `/var/run/docker.sock` (ce qui accorde au conteneur un accĂšs root Ă  l’hĂŽte — consultez l’avertissement relatif au profil `cli` ci-dessus), dĂ©finissez `DOCKER_UPDATE_TRIGGER_FILE` sur le service OmniRoute et exĂ©cutez un observateur sur l’hĂŽte : ```yaml services: omniroute: environment: DOCKER_UPDATE_TRIGGER_FILE: /app/data/update/docker-update.request volumes: - ./data:/app/data ``` Le chemin doit se trouver dans `DATA_DIR` (les valeurs relatives sont rĂ©solues par rapport Ă  celui-ci) ; tout autre chemin est rejetĂ©. **Mettre Ă  jour maintenant** Ă©crit alors `{ "requestedAt": "...", "targetVersion": "X.Y.Z" }` dans ce fichier, rĂ©pond avec le code `202`, puis le tableau de bord interroge `/api/system/version` jusqu’à ce que la nouvelle version soit active avant de se recharger. Dans ce mode, OmniRoute n’exĂ©cute jamais de commande shell ou Docker. Comme pour toute action de mise Ă  jour, la requĂȘte n’est acceptĂ©e que depuis des clients de la boucle locale ou du rĂ©seau local. Un rĂ©pertoire de donnĂ©es montĂ© par liaison fournit Ă  l’hĂŽte un chemin stable Ă  surveiller. Exemples d’unitĂ©s `systemd` (adaptez `/srv/omniroute` au rĂ©pertoire contenant votre fichier Compose et `data/`) : ```ini # /etc/systemd/system/omniroute-update.path [Unit] Description=Surveiller les demandes de mise Ă  jour du tableau de bord OmniRoute [Path] PathExists=/srv/omniroute/data/update/docker-update.request [Install] WantedBy=multi-user.target ``` ```ini # /etc/systemd/system/omniroute-update.service [Unit] Description=RĂ©cupĂ©rer et recrĂ©er le conteneur OmniRoute [Service] Type=oneshot WorkingDirectory=/srv/omniroute ExecStartPre=/usr/bin/rm -f /srv/omniroute/data/update/docker-update.request ExecStart=/bin/sh -c 'docker compose pull omniroute || (sleep 15 && docker compose pull omniroute)' ExecStart=/usr/bin/docker compose up -d --force-recreate omniroute ``` ```bash sudo systemctl daemon-reload sudo systemctl enable --now omniroute-update.path ``` Le service supprime la demande avant de rĂ©cupĂ©rer l’image afin qu’une exĂ©cution ayant Ă©chouĂ© ne provoque pas de boucle. Sans `DOCKER_UPDATE_TRIGGER_FILE`, le tableau de bord conserve le processus de commande manuelle. ## DisponibilitĂ© : SQLite par dĂ©faut ne prend en charge qu’un seul rĂ©plica La configuration Docker / Kubernetes standard d’OmniRoute repose sur **un processus Node + un seul processus d’écriture SQLite**. La haute disponibilitĂ© n’est **pas prise en charge** avec cette topologie. | Contrainte | ConsĂ©quence | | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Processus d’écriture unique | N’exĂ©cutez **pas** plusieurs rĂ©plicas utilisant le mĂȘme fichier SQLite. Cela corrompt la base de donnĂ©es. | | RecrĂ©ation / redĂ©marrage / interruption par HEALTHCHECK | **Interruption totale** des flux SSE en cours, des sessions du tableau de bord et de l’état en mĂ©moire. Chaque client connectĂ© est dĂ©connectĂ©. Les nouvelles requĂȘtes effectuĂ©es pendant la pĂ©riode sans point de terminaison reçoivent du proxy inverse une erreur **`502 Bad Gateway: Unknown error`**, et non une rĂ©ponse JSON d’OmniRoute — les clients ne peuvent pas la distinguer d’une dĂ©faillance de fournisseur (#11015). | | MĂȘme boucle d’évĂ©nements que `/healthz` | Une opĂ©ration intensive sur le catalogue ou un cycle de compression peut retarder les sondes ; un dĂ©lai d’expiration court redĂ©marre alors l’**unique** rĂ©plica. | **Matrice des sondes** (voir Ă©galement [Recommandations relatives aux sondes Kubernetes](../ops/MONITORING_GUIDE.md#kubernetes-probe-recommendations)) : | Sonde | Cible | À ne pas utiliser | | --------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | ActivitĂ© | TCP sur `PORT` (par dĂ©faut `20128`) ou HTTP souple `/healthz` | `/api/monitoring/health` | | DisponibilitĂ© | HTTP `GET /healthz` | Des dĂ©lais d’expiration courts qui considĂšrent une boucle d’évĂ©nements occupĂ©e comme dĂ©faillante | | Approfondie / humaine | `/api/monitoring/health` | Sonde d’activitĂ© automatisĂ©e du kubelet | **Mises Ă  niveau :** attendez-vous Ă  ce que chaque session soit interrompue. Drainez les clients si possible ; aucune mise Ă  jour progressive n’est possible avec la configuration SQLite par dĂ©faut. La combinaison de Compose `restart: unless-stopped` et du `HEALTHCHECK` Docker remplace Ă©galement l’unique processus lorsque le conteneur est Unhealthy — avec le mĂȘme rayon d’impact. Extrait Kubernetes pour un **seul rĂ©plica** (Recreate est requis ; n’augmentez pas `replicas` pour un seul fichier SQLite) : ```yaml 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 pause `preStop` permet Ă  kube de retirer les points de terminaison du Service avant SIGTERM, afin que le **nouveau** trafic cesse d'atteindre le processus en cours d'arrĂȘt. Les flux SSE `/v1/responses` en cours sont drainĂ©s pendant une durĂ©e maximale de `SHUTDOWN_TIMEOUT_MS` (30 s par dĂ©faut) au moyen de baux d'admission heavyweight (#11015). Les nouvelles requĂȘtes qui atteignent encore le processus reçoivent `503` + `Retry-After: 5`. L'intervalle sans point de terminaison propre Ă  Recreate, jusqu'Ă  ce que le remplacement soit prĂȘt, reste une interruption totale de service — cela tient Ă  la topologie SQLite, et non Ă  une mauvaise configuration des sondes. Postgres externe / haute disponibilitĂ© multi-writer ne constitue **pas** une voie standard documentĂ©e. Si vous avez besoin de haute disponibilitĂ©, conservez un seul rĂ©plica ou utilisez une topologie que le projet a testĂ©e et documentĂ©e sĂ©parĂ©ment. Les travaux relatifs Ă  Postgres/MySQL sont suivis dans [#8075](https://github.com/diegosouzapw/OmniRoute/issues/8075). Jusqu'Ă  leur livraison, la seule maniĂšre prise en charge de multiplier la capacitĂ© pour les requĂȘtes `/v1/responses` **volumineuses** consiste Ă  utiliser N processus indĂ©pendants (section suivante), et non `replicas > 1` sur un mĂȘme volume. ## Mise Ă  l’échelle horizontale : N processus indĂ©pendants Un processus Node correspond Ă  **un tas V8**. Deux requĂȘtes d’agent de codage `POST /v1/responses` (RTK + Caveman) qui se chevauchent, d’environ 3 MiB / 750k tokens chacune, font avorter ce tas Ă  environ 12 Gi (`FATAL ERROR: Reached heap limit`) et peuvent provoquer un OOM dans un cgroup de 16 Gi. Voir [#7849](https://github.com/diegosouzapw/OmniRoute/issues/7849). Cette mesure constitue un avertissement relatif au **budget mĂ©moire**, et non une limite maximale stricte du produit fixĂ©e Ă  deux longues requĂȘtes `/v1/responses` simultanĂ©es. L’admission des conversations lourdes est contrĂŽlĂ©e par un budget d’octets d’ingestion calculĂ© automatiquement (`OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES`, `src/shared/middleware/admissionBudget.ts`) et dimensionnĂ© Ă  partir de cette mĂȘme limite V8/cgroup — le remplacer par une valeur supĂ©rieure (ou dĂ©finir l’ancienne limite `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` fondĂ©e sur le nombre de requĂȘtes) sur un processus dĂ©jĂ  dimensionnĂ© rĂ©introduit cet arrĂȘt. Les petites conversations, `/healthz`, `/v1/models` et MCP ne sont **pas** concernĂ©s par cette limite. ### Un seul processus : plus de deux longues requĂȘtes `/v1/responses` Un processus **sain** (tas infĂ©rieur Ă  `OMNIROUTE_CHAT_ADMISSION_HEAP_SHED_RATIO`, valeur par dĂ©faut `0.75`) **peut** exĂ©cuter simultanĂ©ment plus de deux longues requĂȘtes `POST /v1/responses` lorsque le budget d’octets en cours Ă  l’échelle du processus (`OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES` / #10110) dispose encore de suffisamment de capacitĂ©. Les corps dont la taille est supĂ©rieure ou Ă©gale Ă  `OMNIROUTE_CHAT_LARGE_BODY_BYTES` (256 KiB par dĂ©faut) acquiĂšrent le mĂȘme bail de traitement lourd que les requĂȘtes Ă  structure complexe et utilisent le mĂȘme mĂ©canisme d’échappement `tryAcquireHealthyHeadroom` de [#10437](https://github.com/diegosouzapw/OmniRoute/pull/10437) (`OMNIROUTE_CHAT_ADMISSION_HEALTHY_HEADROOM`). La prise en charge de dizaines de clients SSE longs simultanĂ©s (les opĂ©rateurs en nĂ©cessitent souvent 40 Ă  50) est une question de **budget mĂ©moire** — dimensionnez le tas, les emplacements principaux/de rĂ©serve et `OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES` — et non une limite produit stricte de « 2 maximum ». Un tas sous pression continue de dĂ©lester la charge avec des rĂ©ponses `503` rĂ©essayables afin d’éviter le retour du problĂšme #7849. Pour **multiplier les tas** (anciens espaces V8 indĂ©pendants) **dĂšs aujourd’hui** : | À faire | À ne pas faire | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | ExĂ©cuter **N conteneurs/pods**, chacun avec son **propre** `DATA_DIR` / volume | DĂ©finir `replicas > 1` pour un mĂȘme fichier SQLite | | Dimensionner les requĂȘtes lourdes en cours et la rĂ©serve saine en fonction du budget du tas / des octets en cours ; 1 Ă  2 est la valeur par dĂ©faut prudente de #7849, pas une limite produit stricte | Attribuer 8× plus de RAM Ă  un processus avec une limite illimitĂ©e | | Facultatif : `QUOTA_STORE_DRIVER=redis` + `QUOTA_STORE_REDIS_URL` pour des **compteurs de quota partagĂ©s** | ConsidĂ©rer Redis comme un SQLite partagĂ© — ce n’est pas le cas | | Dupliquer les secrets des fournisseurs dans chaque instance (ou accepter des tableaux de bord partitionnĂ©s) | S’attendre Ă  un tableau de bord / journal d’appels unique entre les instances | | Placer n’importe quel Ă©quilibreur de charge en frontal ; une affinitĂ© par clĂ© d’API ou session suffit | Exiger un middleware tenant compte de la taille et propre Ă  un fournisseur | MatĂ©riel : le nombre de longues requĂȘtes `/v1/responses` simultanĂ©es par instance est une question de **budget mĂ©moire** (tas + octets en cours / #10110). `N` `DATA_DIR` indĂ©pendants multiplient toujours les tas : la RAM de l’hĂŽte doit couvrir `N × cgroup`, et non « un pod de 16 Gi avec N=8 ». N’utilisez jamais `replicas > 1` sur un mĂȘme fichier SQLite. Exemple Compose (deux tas, deux volumes — pas `deploy.replicas: 2`) : ```yaml 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 densitĂ© au sein d’un processus (compression hors de l’isolat HTTP) est traitĂ©e dans [#11023](https://github.com/diegosouzapw/OmniRoute/issues/11023). Un cluster logique unique reposant sur un Ă©tat durable partagĂ© est traitĂ© dans [#8075](https://github.com/diegosouzapw/OmniRoute/issues/8075). ## Erreurs rĂ©gionales Gemini dans Docker Google AI Studio / l’API Gemini peut renvoyer une erreur HTTP 400 avec FAILED_PRECONDITION et `User location is not supported for the API use.` Une requĂȘte rĂ©ussie sur l’hĂŽte ne prouve pas que le conteneur utilise la mĂȘme route sortante. L’ordre DNS, la connectivitĂ© IPv4/IPv6, le routage VPN et les proxys configurĂ©s peuvent diffĂ©rer. Consultez [les rĂ©gions prises en charge par Google](https://ai.google.dev/gemini-api/docs/available-regions) ainsi que la route de connexion rĂ©elle ; cette erreur seule n’indique pas que la clĂ© API est incorrecte. ### PrivilĂ©gier un proxy propre Ă  la connexion Utilisez la [configuration de proxy par connexion](../ops/PROXY_GUIDE.md#4-level-proxy-system) d’OmniRoute pour la connexion Gemini concernĂ©e, puis relancez **Tester la connexion** et une petite requĂȘte avec le mĂȘme modĂšle. Ainsi, le changement de routage reste limitĂ© Ă  cette connexion. VĂ©rifiez que le proxy est accessible depuis le conteneur et que la connexion le sĂ©lectionne effectivement. La modification de la route ne garantit pas l’éligibilitĂ© rĂ©gionale auprĂšs du fournisseur en amont. ### Comparer la mise en rĂ©seau de l’hĂŽte et du conteneur Conservez une clĂ©, un modĂšle et une requĂȘte identiques lorsque vous comparez les rĂ©sultats authentifiĂ©s ; ne publiez jamais d’identifiants, de mots de passe de proxy ou d’en-tĂȘtes d’autorisation complets dans un ticket. Commencez par vĂ©rifier quelles familles d’adresses sont proposĂ©es par le rĂ©solveur du systĂšme d’exploitation, en utilisant la mĂȘme commande sur l’hĂŽte et dans le conteneur : ```bash node -e 'require("node:dns").lookup("generativelanguage.googleapis.com", {all: true}, (error, addresses) => { if (error) { console.error(error.code); process.exitCode = 1; return; } console.log(addresses.map(({family}) => family)); })' docker compose exec omniroute node -e 'require("node:dns").lookup("generativelanguage.googleapis.com", {all: true}, (error, addresses) => { if (error) { console.error(error.code); process.exitCode = 1; return; } console.log(addresses.map(({family}) => family)); })' ``` Remplacez `omniroute` par le service que vous exĂ©cutez (par exemple, `omniroute-web`). Ces commandes affichent les familles d’adresses sans identifiants ni adresses IP. Une valeur `6` renvoyĂ©e indique uniquement un rĂ©sultat DNS IPv6 : elle ne prouve **pas** qu’une route IPv6 utilisable ou qu’un accĂšs Ă  l’API existe. Lorsque `curl` est installĂ©, comparez `curl -4 -I https://generativelanguage.googleapis.com` Ă  `curl -6 -I https://generativelanguage.googleapis.com` dans les deux environnements. Une rĂ©ponse HTTP prouve la connectivitĂ© pour ce test, mĂȘme s’il s’agit d’une erreur non authentifiĂ©e ; seule la requĂȘte authentifiĂ©e au modĂšle permet de tester l’éligibilitĂ© Ă  Gemini. ### Alternative au niveau de l’hĂŽte : IPv6 fonctionnel et stratĂ©gie du rĂ©solveur L’auteur du signalement [#12762](https://github.com/diegosouzapw/OmniRoute/issues/12762) a rĂ©tabli l’accĂšs dans son environnement en activant IPv6 pour les conteneurs et en modifiant la sĂ©lection des adresses de la glibc. ConsidĂ©rez cette solution comme une alternative propre Ă  l’environnement. VĂ©rifiez le bon fonctionnement d’IPv6 sur l’hĂŽte, la sortie et le routage des conteneurs ainsi que les rĂšgles du pare-feu avant d’ajuster les prĂ©fĂ©rences du rĂ©solveur. Une adresse ULA privĂ©e ne suffit pas Ă  Ă©tablir une connectivitĂ© IPv6 publique. Pour les services dĂ©jĂ  rattachĂ©s au rĂ©seau par dĂ©faut de Compose, ce fragment active IPv6 sur ce rĂ©seau ; conservez le reste de la configuration de votre service, notamment les ports et les volumes : ```yaml networks: default: enable_ipv6: true ``` Pour un rĂ©seau nommĂ©, activez cette option sur le rĂ©seau auquel le service est effectivement rattachĂ©. Docker peut allouer un sous-rĂ©seau ULA ; choisissez un sous-rĂ©seau explicite sans chevauchement uniquement si votre rĂ©seau l’exige. Consultez [la mise en rĂ©seau IPv6 de Docker](https://docs.docker.com/engine/daemon/ipv6/) et [les options rĂ©seau de Compose](https://docs.docker.com/reference/compose-file/networks/#enable_ipv6). Sur une **image basĂ©e sur la glibc**, `/etc/gai.conf` peut modifier la sĂ©lection des adresses. Le Dockerfile actuel du dĂ©pĂŽt utilise Debian ; les images personnalisĂ©es basĂ©es sur musl ne partagent pas ce mĂ©canisme. L’ajustement signalĂ© remplace l’étiquette ULA `label fc00::/7 6` par `label fc00::/7 1`. Partez de la table de stratĂ©gie complĂšte de l’image et conservez ses autres entrĂ©es : l’ajout d’une entrĂ©e `label` ou `precedence` remplace cette table par dĂ©faut ; un fichier contenant uniquement la ligne modifiĂ©e est donc insuffisant. La [rĂ©fĂ©rence de configuration de la glibc](https://github.com/bminor/glibc/blob/master/posix/gai.conf) documente cette sĂ©mantique. Montez le fichier vĂ©rifiĂ© en lecture seule sur `/etc/gai.conf` et recrĂ©ez le service pour appliquer la modification. Cette modification affecte la sĂ©lection des adresses par le systĂšme d’exploitation pour **tout le trafic sortant de ce conteneur**. Elle ne force pas toutes les applications Ă  choisir IPv6 : l’ordre DNS et la sĂ©lection des connexions de Node ont Ă©galement leur importance. En particulier, `--dns-result-order=ipv4first` privilĂ©gie IPv4 et ne rĂ©sout pas une dĂ©faillance propre Ă  IPv4. Consultez [l’ordre DNS de Node](https://nodejs.org/api/dns.html#dnssetdefaultresultorderorder). Testez de nouveau Gemini et vos autres fournisseurs aprĂšs toute modification au niveau de l’hĂŽte. Pour revenir en arriĂšre, supprimez le montage personnalisĂ© de `gai.conf`, restaurez la configuration rĂ©seau prĂ©cĂ©dente et recrĂ©ez le service ou le rĂ©seau concernĂ© pendant une fenĂȘtre de maintenance. La recrĂ©ation d’un rĂ©seau peut interrompre les autres conteneurs qui y sont rattachĂ©s ; ne supprimez pas le volume de donnĂ©es persistant. ## Remarques importantes - **Mode WAL de SQLite :** il faut laisser `docker stop` se terminer afin qu’OmniRoute puisse enregistrer les derniĂšres modifications dans `storage.sqlite` lors du point de contrĂŽle. Les fichiers Compose fournis dĂ©finissent dĂ©jĂ  un dĂ©lai d’arrĂȘt gracieux de 40 s. Si vous exĂ©cutez directement l’image, conservez `--stop-timeout 40`. - **`DISABLE_SQLITE_AUTO_BACKUP` :** dĂ©finissez cette variable sur `true` si les sauvegardes rĂ©guliĂšres/avant Ă©criture sont gĂ©rĂ©es en externe. Les migrations de bases de donnĂ©es existantes nĂ©cessitent toujours leur propre instantanĂ© de sĂ©curitĂ© durable ainsi qu’un mĂ©canisme de protection contre les migrations en masse. - **Persistance des donnĂ©es :** montez toujours un volume sur `/app/data` afin de conserver votre base de donnĂ©es, vos clĂ©s et vos configurations aprĂšs le redĂ©marrage des conteneurs. - **Configuration du port :** remplacez la valeur de la variable d’environnement `PORT` pour modifier le port par dĂ©faut `20128`. ## Voir aussi - [Guide de dĂ©ploiement sur une VM](../ops/VM_DEPLOYMENT_GUIDE.md) — Configuration d’une VM avec nginx et Cloudflare - [Guide de dĂ©ploiement sur Fly.io](../ops/FLY_IO_DEPLOYMENT_GUIDE.md) — DĂ©ployer sur Fly.io - [Configuration de l’environnement](../reference/ENVIRONMENT.md) — RĂ©fĂ©rence complĂšte du fichier `.env`