# How to run Gemini CLI in a cloud sandbox Install `@google/gemini-cli` with npm in a Linux microVM, keep the Gemini key as a secret, run `gemini -p` with `--approval-mode yolo`. **Runtime lets Gemini CLI run with every approval switched on, while your Google key stays outside the machine.** Each sandbox is a Firecracker microVM with its own Linux kernel, Ubuntu 24.04 and Node.js 24. That meets Gemini CLI's requirement of Node.js 20 or later. The sandbox holds only a placeholder for `GEMINI_API_KEY`. The host's proxy swaps in the real key only on requests to Google's Gemini API, and nowhere else. While the agent waits on the model, a 2 vCPU, 4 GiB sandbox bills $0.03125 an hour. Gemini CLI 0.65.0 was the current npm release on 25 October 2026. | Goal | Where Gemini CLI runs | How it reaches Runtime | | ------------------------------------------------------- | --------------------- | ----------------------------------- | | Let the agent change a repo with full shell access | Inside a sandbox | The SDK drives it from your code | | Give Gemini CLI on your laptop a safe place to run code | On your machine | Runtime's MCP server, as a tool set | ## Why yolo mode needs a sandbox In headless mode, nobody is present to approve a shell command. A useful run therefore needs `--approval-mode yolo`. Google's CLI reference describes this mode as auto-approving all actions. On a laptop or a CI runner, that means the model can run anything the user can. In a Runtime sandbox, the blast radius is one disposable virtual machine. It has its own kernel and no route to your network. Its outbound traffic is limited to the hosts you list. It holds no real credential on its disk. ## Keep the Gemini key out of the sandbox Gemini CLI reads an AI Studio key from `GEMINI_API_KEY`. Store it once as a Runtime secret tied to the Gemini API's host: ```bash no-run printf %s "$GEMINI_API_KEY" | npx withruntime secrets set GEMINI_API_KEY --host generativelanguage.googleapis.com ``` From then on, every sandbox in your account starts with `GEMINI_API_KEY` set to a placeholder such as `rtsec_3f9c…`. The proxy adds the real key to the URL or headers of HTTPS requests to `generativelanguage.googleapis.com` only. So an agent that is tricked into posting its environment elsewhere sends a string that unlocks nothing ([secrets sandboxes never see](/docs/security#secrets-sandboxes-never-see)). ## Run a task and collect the diff ```ts check import { writeFile } from "node:fs/promises"; import { Sandbox } from "withruntime"; const repo = "https://github.com/your-org/your-repo"; const task = "Add input validation to the signup handler and write a test for it."; await using sbx = await Sandbox.create({ diskMiB: 8192, timeoutSeconds: 3600 }); const setup = { check: true, timeoutMs: 600_000 } as const; await sbx.exec("npm install -g --prefix /workspace/.local @google/gemini-cli", setup); await sbx.exec(["git", "clone", "--depth", "1", repo, "/workspace/app"], setup); // Only the Gemini API and the npm registry are reachable from here. await sbx.network.set({ internet: true, allow: ["generativelanguage.googleapis.com", "registry.npmjs.org"], }); const run = await sbx.exec( ["gemini", "-p", task, "--approval-mode", "yolo", "--skip-trust", "--output-format", "json"], { cwd: "/workspace/app", timeoutMs: 1_800_000, onStderr: (text) => process.stderr.write(text) }, ); const report = JSON.parse(run.stdout); console.log(run.exitCode, report.response, report.stats); await sbx.exec("git add -A && git diff --cached > /workspace/gemini.patch", { cwd: "/workspace/app", check: true, }); await writeFile("gemini.patch", await sbx.files.readText("/workspace/gemini.patch")); ``` ```python check import sys, json from withruntime import Sandbox repo = "https://github.com/your-org/your-repo" task = "Add input validation to the signup handler and write a test for it." with Sandbox.create(disk_mib=8192, timeout_seconds=3600) as sbx: sbx.exec("npm install -g --prefix /workspace/.local @google/gemini-cli", check=True, timeout_ms=600_000) sbx.exec(["git", "clone", "--depth", "1", repo, "/workspace/app"], check=True, timeout_ms=600_000) sbx.network.set(internet=True, allow=["generativelanguage.googleapis.com", "registry.npmjs.org"]) run = sbx.exec( ["gemini", "-p", task, "--approval-mode", "yolo", "--skip-trust", "--output-format", "json"], cwd="/workspace/app", timeout_ms=1_800_000, on_stderr=sys.stderr.write, ) report = json.loads(run.stdout) print(run.exit_code, report["response"], report["stats"]) sbx.exec("git add -A && git diff --cached > /workspace/gemini.patch", cwd="/workspace/app", check=True) with open("gemini.patch", "w") as file: file.write(sbx.files.read_text("/workspace/gemini.patch")) ``` Notes on the run: - **The install** puts `gemini` in `/workspace/.local/bin`. That directory is the first one on the sandbox's `PATH`. No `sudo` is needed. - **`-p`** forces non-interactive mode. The task is passed as one array element, so no shell reads its quotes. - **`--skip-trust`** trusts the checkout for this session, so the folder-trust check never waits for an answer. - **`--output-format json`** prints one object. `response` holds the final answer, and `stats` holds the token use and latency. - **The patch** is written to a file and read back. This keeps a large change clear of the 64 KiB limit on command output. ## Headless reference From Google's headless and CLI reference pages, read on 25 October 2026: | Flag or code | Meaning | | ----------------------------- | --------------------------------------------------------- | | `-p`, `--prompt` | Runs one prompt without the terminal UI | | `--approval-mode yolo` | Approves every tool call; `auto_edit` approves edits only | | `--yolo`, `-y` | Deprecated spelling of the same | | `--output-format json` | One object with `response`, `stats` and `error` | | `--output-format stream-json` | JSONL events: `init`, `message`, `tool_use`, `result` | | `--include-directories a,b` | Adds more folders to the workspace | | Exit `0`, `1`, `42`, `53` | Success, API error, bad input, turn limit exceeded | Branch on the exit code in your host code. A `53` means the task needs more turns. It does not mean the model failed. For a follow-up task on the same checkout, [pause the sandbox](/how-to/pause-and-resume-a-sandbox) between runs. Files, memory and processes survive, and compute billing stops. ## Let Gemini CLI call Runtime In the reverse setup, Gemini CLI stays on your own machine and hands its code execution to Runtime. Add the MCP server once for your user: ```bash no-run gemini mcp add --scope user runtime npx -y withruntime mcp ``` That writes an entry to `~/.gemini/settings.json`. To edit the file by hand, add this: ```json no-run { "mcpServers": { "runtime": { "command": "npx", "args": ["-y", "withruntime", "mcp"] } } } ``` The first tool call prints a link and a short code. Approve **Connect agent** in the browser. The full tool list then loads, and the file holds no key ([MCP](/docs/mcp#add-it-to-your-agent)). A laptop already signed in with `npx withruntime login` skips that step. Gemini CLI also accepts remote servers by `httpUrl`. `https://api.withruntime.com/mcp` works with browser sign-in instead of the bridge ([remote connection](/docs/mcp#remote-connection)). | Tool | What Gemini CLI can do with it | | ----------------------------------- | ------------------------------------------------- | | `runtime_sandboxes_create` | Start a microVM and wait until it is running | | `runtime_sandboxes_exec` | Run a command and read its exit code and output | | `runtime_sandboxes_interpreter_run` | Keep a Python or JavaScript session with charts | | `runtime_sandboxes_network` | Cut the sandbox's internet or narrow it to a list | | `runtime_sandboxes_manage` | Pause, wake, extend or stop it | ## What a run costs CPU is billed on use at $0.025 per vCPU-hour, with a floor of a twentieth of a vCPU. Memory is billed at $0.0075 per GiB-hour of what you reserve. For 2 vCPUs and 4 GiB, that is $0.03125 an hour while Gemini thinks. It rises to $0.08 an hour only while both cores run tests ([pricing](/docs/pricing)). Google bills the tokens on your key. Every account has 100 free hours of a 1 GB machine in its first 30 days, with no card needed: ```bash no-run npx withruntime sandboxes run -- node --version ``` The same pattern for other agents is in [Codex](/integrations/codex) and [Qwen Code](/integrations/qwen-code), which began as a fork of Gemini CLI. [A sandbox for coding agents](/use-cases/coding-agent-sandbox) covers running many tasks side by side. ## Sources Checked 25 October 2026. - [Gemini CLI on GitHub](https://github.com/google-gemini/gemini-cli): `npm install -g @google/gemini-cli` and `GEMINI_API_KEY` - [Installation](https://github.com/google-gemini/gemini-cli/blob/main/docs/get-started/installation.mdx): Node.js 20.0.0 or later - [Headless mode reference](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/headless.md): output formats, JSON fields and exit codes - [CLI reference](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/cli-reference.md): `--approval-mode`, `--skip-trust`, `--include-directories`, `gemini mcp add` - [MCP servers](https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md): `mcpServers` in `~/.gemini/settings.json`, `httpUrl` and OAuth - [@google/gemini-cli on npm](https://www.npmjs.com/package/@google/gemini-cli): version 0.65.0 Facts on this page were checked on 25 October 2026.