How to run Goose in a cloud sandbox
Install the goose CLI with its download script in a Linux microVM, set provider and model by environment, and run goose run -t.
Runtime gives goose a disposable Linux machine and removes the keyring problem at the same time. goose's Docker guide notes that its keyring settings do not work on Ubuntu in a container and has you pass the API key as an environment variable instead. On Runtime that variable holds only a placeholder: the key is a Runtime secret, and the host's proxy adds the real value on requests to the provider. Every sandbox is a Firecracker microVM with its own kernel and Ubuntu 24.04. While goose waits on the model, 2 vCPUs and 4 GiB cost $0.03125 an hour. The newest stable tag on 25 September 2026 was v1.52.0.
| Direction | goose runs | Runtime is |
|---|---|---|
| Headless task on a repo | In a sandbox | The machine, driven by the SDK |
| goose session on your laptop | On your machine | A stdio extension with sandbox tools |
Store the provider key
goose reads provider settings from the environment: GOOSE_PROVIDER,
GOOSE_MODEL, and the provider's own key variable, which for Anthropic is
ANTHROPIC_API_KEY. Only the key is sensitive, so only the key becomes a
secret:
Terminalprintf %s "$ANTHROPIC_API_KEY" | npx withruntime secrets set ANTHROPIC_API_KEY --host api.anthropic.comEach sandbox of your account now starts with ANTHROPIC_API_KEY set to a
placeholder. GOOSE_DISABLE_KEYRING tells goose not to look for a system
keyring, and the proxy puts the real key on HTTPS requests to
api.anthropic.com only
(secrets sandboxes never see).
Any other provider goose supports works the same way with its own variable
and host.
A headless run from your code
TypeScriptimport { writeFile } from "node:fs/promises";import { Sandbox } from "withruntime";const repo = "https://github.com/your-org/your-repo";const task = "Upgrade the project to the current major version of its test runner and fix what breaks.";const installGoose = "curl -fsSL https://github.com/aaif-goose/goose/releases/download/stable/download_cli.sh | CONFIGURE=false bash";await using sbx = await Sandbox.create({ diskMiB: 8192, timeoutSeconds: 3600 });const once = { check: true, timeoutMs: 600_000 } as const;await sbx.exec("sudo apt-get update && sudo apt-get install -y bzip2", once);await sbx.exec(installGoose, once);await sbx.exec(["git", "clone", "--depth", "1", repo, "/workspace/app"], once);// goose is installed; from here it may reach the model API and nothing else.await sbx.network.set({ internet: true, allow: ["api.anthropic.com"] });const run = await sbx.exec( [ "goose", "run", "--no-session", "--with-builtin", "developer", "--output-format", "json", "--max-turns", "60", "-t", task, ], { cwd: "/workspace/app", env: { GOOSE_PROVIDER: "anthropic", GOOSE_MODEL: "claude-sonnet-4-5-20250929", GOOSE_MODE: "auto", GOOSE_DISABLE_KEYRING: "1", GOOSE_DISABLE_SESSION_NAMING: "true", }, timeoutMs: 1_800_000, onStderr: (text) => process.stderr.write(text), },);console.log(run.exitCode, JSON.parse(run.stdout));await sbx.exec("git add -A && git diff --cached > /workspace/goose.patch", { cwd: "/workspace/app", check: true,});await writeFile("goose.patch", await sbx.files.readText("/workspace/goose.patch"));Pythonimport sys, jsonfrom withruntime import Sandboxrepo = "https://github.com/your-org/your-repo"task = "Upgrade the project to the current major version of its test runner and fix what breaks."install_goose = ("curl -fsSL https://github.com/aaif-goose/goose/releases/download/" "stable/download_cli.sh | CONFIGURE=false bash")with Sandbox.create(disk_mib=8192, timeout_seconds=3600) as sbx: sbx.exec("sudo apt-get update && sudo apt-get install -y bzip2", check=True, timeout_ms=600_000) sbx.exec(install_goose, 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=["api.anthropic.com"]) run = sbx.exec( ["goose", "run", "--no-session", "--with-builtin", "developer", "--output-format", "json", "--max-turns", "60", "-t", task], cwd="/workspace/app", env={ "GOOSE_PROVIDER": "anthropic", "GOOSE_MODEL": "claude-sonnet-4-5-20250929", "GOOSE_MODE": "auto", "GOOSE_DISABLE_KEYRING": "1", "GOOSE_DISABLE_SESSION_NAMING": "true", }, timeout_ms=1_800_000, on_stderr=sys.stderr.write, ) print(run.exit_code, json.loads(run.stdout)) sbx.exec("git add -A && git diff --cached > /workspace/goose.patch", cwd="/workspace/app", check=True) with open("goose.patch", "w") as file: file.write(sbx.files.read_text("/workspace/goose.patch"))Step by step:
bzip2comes first because the Linux release is a.tar.bz2; goose's install guide suggests installing it when extraction fails.CONFIGURE=falseskips the interactivegoose configurestep. The script installs to$HOME/.local/bin, which in a sandbox is/workspace/.local/bin, already first onPATH.- The allow list is one host. goose ships as a single binary, so once it is installed it needs no package registry. Add the registries your project's tests install from.
--with-builtin developerloads goose's built-in developer extension, which gives it shell and file editing. With no config file, nothing else is loaded.GOOSE_MODE=autolets goose run tools without asking, andGOOSE_DISABLE_SESSION_NAMINGsaves the extra model call goose otherwise makes to name the session.--no-sessionwrites no session file;--max-turnscaps the turns taken without user input (the default is 1000).--output-format jsonreturns the result once the run completes; usestream-jsonto watch events as they happen.
goose run options worth knowing
From goose's CLI command reference, 25 September 2026:
| Option | Purpose |
|---|---|
-t, --text <TEXT> |
The instruction |
-i, --instructions <FILE> |
Instructions from a file |
--recipe <FILE> |
A recipe with a prompt field, for repeatable jobs |
--provider, --model |
Override GOOSE_PROVIDER and GOOSE_MODEL |
-q, --quiet |
Print only the model's response |
--output-format json, stream-json |
Structured output after or during the run |
--max-tool-repetitions <n> |
Stops identical tool calls repeating in a loop |
A recipe is the goose way to repeat a job across many repos: one YAML file
holds the prompt, instructions and extensions, and each sandbox runs
goose run --recipe. Keep a checkout between recipes by
pausing the sandbox rather than stopping
it.
Add Runtime as a goose extension
goose calls MCP servers extensions. To give a goose session on your laptop Runtime's tools for one run:
Terminalgoose session --with-extension "runtime:npx -y withruntime mcp"The runtime: prefix names the extension, so its tools appear as
runtime__runtime_sandbox_create and so on rather than under npx. To keep
it, add a stdio extension to ~/.config/goose/config.yaml:
YAMLextensions: runtime: type: stdio name: runtime enabled: true cmd: npx args: ["-y", "withruntime", "mcp"] envs: {} env_keys: [] timeout: 300The first Runtime call prints a link and a code; approve Connect agent in
the browser and every tool loads, with no key in the YAML
(MCP). goose also takes remote servers as
streamable_http extensions, which fits https://api.withruntime.com/mcp.
| Tool | What goose gets |
|---|---|
runtime_sandbox_create |
A fresh microVM, ready when the call returns |
runtime_sandbox_exec |
Commands run away from your laptop |
runtime_sandbox_desktop_screenshot |
A look at a desktop running in the sandbox |
runtime_secrets_set |
Keys for the sandbox to use without seeing |
Cost
Runtime charges for CPU a sandbox actually uses, $0.025 per vCPU-hour with a floor of a twentieth of a vCPU, and $0.0075 per GiB-hour for reserved memory. A 2 vCPU, 4 GiB goose run is $0.03125 an hour while the model is working and $0.08 an hour at full CPU (pricing). The model provider bills tokens on your key. The trial has 50 free sandbox hours, no card needed:
Terminalnpx withruntime sandbox run --trial -- uname -aOther open agents on Runtime: OpenCode and OpenHands. For many repos at once, see a sandbox for coding agents.
Sources
Checked 25 September 2026.
- Install goose:
download_cli.sh,CONFIGURE=false, thebzip2tip - download_cli.sh:
installs to
$HOME/.local/bin - Headless goose:
goose run,--no-session,GOOSE_MODE=auto,GOOSE_DISABLE_SESSION_NAMING - goose in Docker: keys by environment because the keyring does not work on Ubuntu in Docker
- CLI commands:
runoptions,--with-extensionnaming - Environment variables:
GOOSE_PROVIDER,GOOSE_MODEL,GOOSE_DISABLE_KEYRING - Config files:
~/.config/goose/config.yamland extension types - Releases: tag v1.52.0
Facts on this page were checked on 25 September 2026.