# 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: ```bash no-run printf %s "$ANTHROPIC_API_KEY" | npx withruntime secrets set ANTHROPIC_API_KEY --host api.anthropic.com ``` Each 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](/docs/security#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 ```ts check import { 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")); ``` ```python check import sys, json from withruntime import Sandbox repo = "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: - **`bzip2`** comes first because the Linux release is a `.tar.bz2`; goose's install guide suggests installing it when extraction fails. - **`CONFIGURE=false`** skips the interactive `goose configure` step. The script installs to `$HOME/.local/bin`, which in a sandbox is `/workspace/.local/bin`, already first on `PATH`. - **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 developer`** loads goose's built-in developer extension, which gives it shell and file editing. With no config file, nothing else is loaded. - **`GOOSE_MODE=auto`** lets goose run tools without asking, and `GOOSE_DISABLE_SESSION_NAMING` saves the extra model call goose otherwise makes to name the session. - **`--no-session`** writes no session file; **`--max-turns`** caps the turns taken without user input (the default is 1000). - **`--output-format json`** returns the result once the run completes; use `stream-json` to watch events as they happen. ## goose run options worth knowing From goose's CLI command reference, 25 September 2026: | Option | Purpose | | ------------------------------------- | --------------------------------------------------- | | `-t`, `--text ` | The instruction | | `-i`, `--instructions ` | Instructions from a file | | `--recipe ` | 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 ` | 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](/how-to/pause-and-resume-a-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: ```bash no-run goose 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`: ```yaml no-run extensions: runtime: type: stdio name: runtime enabled: true cmd: npx args: ["-y", "withruntime", "mcp"] envs: {} env_keys: [] timeout: 300 ``` The 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](/docs/mcp#add-it-to-your-agent)). 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](/docs/pricing)). The model provider bills tokens on your key. The trial has 50 free sandbox hours, no card needed: ```bash no-run npx withruntime sandbox run --trial -- uname -a ``` Other open agents on Runtime: [OpenCode](/integrations/opencode) and [OpenHands](/integrations/openhands). For many repos at once, see [a sandbox for coding agents](/use-cases/coding-agent-sandbox). ## Sources Checked 25 September 2026. - [Install goose](https://goose-docs.ai/docs/getting-started/installation): `download_cli.sh`, `CONFIGURE=false`, the `bzip2` tip - [download_cli.sh](https://github.com/aaif-goose/goose/blob/main/download_cli.sh): installs to `$HOME/.local/bin` - [Headless goose](https://goose-docs.ai/docs/tutorials/headless-goose): `goose run`, `--no-session`, `GOOSE_MODE=auto`, `GOOSE_DISABLE_SESSION_NAMING` - [goose in Docker](https://goose-docs.ai/docs/tutorials/goose-in-docker): keys by environment because the keyring does not work on Ubuntu in Docker - [CLI commands](https://goose-docs.ai/docs/guides/goose-cli-commands): `run` options, `--with-extension` naming - [Environment variables](https://goose-docs.ai/docs/guides/environment-variables): `GOOSE_PROVIDER`, `GOOSE_MODEL`, `GOOSE_DISABLE_KEYRING` - [Config files](https://goose-docs.ai/docs/guides/config-files): `~/.config/goose/config.yaml` and extension types - [Releases](https://github.com/aaif-goose/goose/releases): tag v1.52.0 Facts on this page were checked on 25 September 2026.