# Deployment layouts One image, three roles (`JARVIS_ROLE`), selected in `.env`. GPU is added per OS via a compose override picked with `COMPOSE_FILE`. > `COMPOSE_FILE`'s file separator is OS-specific: Linux/macOS use `:`, Windows > uses `;` (a colon collides with the `C:` drive letter). Using `:` on Windows > yields `... The system cannot find the file specified`. If in doubt, leave > `COMPOSE_FILE` unset and pass the files explicitly: > `docker compose -f docker-compose.yml -f docker-compose.gpu-windows.yml up -d`. ## A. All-in-one (single machine) Everything (desktop + Chrome + bridge + bot + TTS) in one container. ``` # .env JARVIS_ROLE=full COMPOSE_FILE=docker-compose.yml:docker-compose.gpu-linux.yml # Ubuntu/macOS (":" ) # COMPOSE_FILE=docker-compose.yml;docker-compose.gpu-windows.yml # Windows 11 (";" ) DISCORD_SELFBOT_TOKEN=... DISCORD_GUILD_ID=... docker compose up -d # Ollama + javis (COMPOSE_FILE adds GPU) ``` ## B. Split: browser host (LAN) + bot on your PC The on-screen Chrome, real mouse/keyboard (xdotool) and screen live on the **browser host**. Your PC runs the **bot** and drives that browser over the internal network — no auth (internal only). ### Browser host (the LAN machine that shows Chrome, e.g. 192.168.10.9) ``` # .env JARVIS_ROLE=browser CDP_BIND=0.0.0.0 BROWSER_CONTROL_BIND=0.0.0.0 CDP_PUBLISH_BIND=0.0.0.0 # no GPU needed → leave COMPOSE_FILE unset (base compose only) docker compose up -d javis # desktop + Chrome + control-server (port 8777) ``` Watch it on this machine’s VNC (`localhost:5901`) / noVNC (`localhost:6080`). ### Bot host (your PC — Ubuntu or Windows 11) ``` # .env JARVIS_ROLE=bot BROWSER_CONTROL_URL=http://192.168.10.9:8777 # the browser host's LAN IP COMPOSE_FILE=docker-compose.yml:docker-compose.gpu-linux.yml # Ubuntu/macOS (":" ) # COMPOSE_FILE=docker-compose.yml;docker-compose.gpu-windows.yml # Windows 11 (";" ) DISCORD_SELFBOT_TOKEN=... DISCORD_GUILD_ID=... docker compose up -d # bot + bridge + TTS + Ollama (GPU per OS) ``` The bot’s `controlBrowser` tool posts commands to `BROWSER_CONTROL_URL`, so "네이버에서 X 검색", "구글로 돌아가" etc. drive the **browser host’s** Chrome with real human-style input (visible on its VNC). ## Windows 11 notes - Install the NVIDIA driver on Windows and enable GPU in Docker Desktop (Settings → Resources → WSL Integration). Use the `gpu-windows.yml` override. - Paths: named volumes are cross-platform. The Gemini OAuth login (for `GEMINI_AUTH=oauth`) is bind-mounted from the project-local `./docker/gemini-oauth` into the container's `~/.gemini`. A project-relative path is used so it resolves the same on Windows Docker Desktop and Linux (`${HOME}` is often unset when compose runs from PowerShell/cmd). Seed it once from a machine with a browser and the logged-in Gemini CLI (`npm i -g @google/gemini-cli`, then `gemini` -> "Sign in with Google"), copying the login state: (Note: as of 2026-06 Google blocks personal Google accounts on this CLI login with "This client is no longer supported for Gemini Code Assist for individuals". Workspace/org accounts may still work; personal accounts should use `GEMINI_AUTH=apikey` with a key from https://aistudio.google.com/app/apikey instead. Real-time search fail-opens to DDG/Brave/Wikipedia either way.) `cp -r ~/.gemini/. docker/gemini-oauth/`. The essential file is `oauth_creds.json` (it holds the refresh token; `GOOGLE_GENAI_USE_GCA=true` forces OAuth, so that is the file the startup readiness check looks for) - copying the whole dir simply also carries the cached account/settings. To reuse an existing host login without copying, set `GEMINI_OAUTH_DIR=~/.gemini` in `.env`. If unseeded, real-time search fail-opens to DDG/Brave and the container logs a `🔑` warning on startup. ## Known limitation Discord Go-Live broadcast of the **browser host's** screen from a **remote** bot is not supported (the bot's WebRTC screen capture is local to the bot machine). Use the browser host's VNC to view it. A full remote-broadcast path is separate, larger work.