# 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, which 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 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.61.0 was the current npm release on 25 September 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, so a useful run needs `--approval-mode yolo`, which Google's CLI reference describes 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: its own kernel, no route to your network, outbound traffic limited to the hosts you list, and 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 of your account starts with `GEMINI_API_KEY` set to a value such as `rtsec_3f9c…`. The proxy puts the real key into the URL or headers of HTTPS requests to `generativelanguage.googleapis.com` only, so an agent 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`, the first directory on the sandbox's `PATH`, without `sudo`. - **`-p`** forces non-interactive mode; the task is passed as one array element, so no shell reinterprets 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` the token use and latency. - **The patch** is written to a file and read back, which 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 September 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, not that 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 The reverse direction keeps Gemini CLI 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: ```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 and the full tool list loads with no key in the file ([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`, so `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_sandbox_create` | Start a microVM and wait until it is running | | `runtime_sandbox_exec` | Run a command and read its exit code and output | | `runtime_sandbox_interpreter_run` | Keep a Python or JavaScript session with charts | | `runtime_sandbox_network_set` | Cut the sandbox's internet or narrow it to a list | | `runtime_sandbox_manage` | Pause, wake, extend or stop it | ## What a run costs CPU is billed on use at $0.025 per vCPU-hour, never below a twentieth of a vCPU, and memory at $0.0075 per GiB-hour of what you reserve. That is $0.03125 an hour for 2 vCPUs and 4 GiB while Gemini thinks, rising to $0.08 an hour only while both cores run tests ([pricing](/docs/pricing)). Google bills the tokens on your key. A new account has 50 sandbox hours free with no card: ```bash no-run npx withruntime sandbox run --trial -- 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 September 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.61.0 Facts on this page were checked on 25 September 2026.