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:
Terminalprintf %s "$GEMINI_API_KEY" | npx withruntime secrets set GEMINI_API_KEY --host generativelanguage.googleapis.comFrom 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).
Run a task and collect the diff
TypeScriptimport { 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"));Pythonimport sys, jsonfrom withruntime import Sandboxrepo = "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
geminiin/workspace/.local/bin. That directory is the first one on the sandbox'sPATH. Nosudois needed. -pforces non-interactive mode. The task is passed as one array element, so no shell reads its quotes.--skip-trusttrusts the checkout for this session, so the folder-trust check never waits for an answer.--output-format jsonprints one object.responseholds the final answer, andstatsholds 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 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:
Terminalgemini mcp add --scope user runtime npx -y withruntime mcpThat writes an entry to ~/.gemini/settings.json. To edit the file by hand,
add this:
JSON{ "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). 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).
| 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). 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:
Terminalnpx withruntime sandboxes run -- node --versionThe same pattern for other agents is in Codex and Qwen Code, which began as a fork of Gemini CLI. A sandbox for coding agents covers running many tasks side by side.
Sources
Checked 25 October 2026.
- Gemini CLI on GitHub:
npm install -g @google/gemini-cliandGEMINI_API_KEY - Installation: Node.js 20.0.0 or later
- Headless mode reference: output formats, JSON fields and exit codes
- CLI reference:
--approval-mode,--skip-trust,--include-directories,gemini mcp add - MCP servers:
mcpServersin~/.gemini/settings.json,httpUrland OAuth - @google/gemini-cli on npm: version 0.65.0
Facts on this page were checked on 25 October 2026.