Skip to content
Runtime

How to run a browser automation agent in a cloud sandbox

Give the agent a Linux desktop in a sandbox. Send it screenshots. Apply the clicks and typing that it chooses.

On Runtime, the browser runs on a machine of its own. You pay for the CPU it uses. A 2 vCPU, 4 GiB sandbox costs $0.03125 per hour while the agent waits on its model. It costs $0.08 per hour with both CPUs busy. The desktop takes clicks, typing and screenshots through the SDK. It streams live to a private link and records to MP4. You have nothing to install on your side.

The short answer

Start a desktop and open the page. Then loop: send a screenshot to the model, and send the model's action back to the desktop.

TypeScriptimport { Sandbox } from "withruntime";type Action =  | { kind: "click"; x: number; y: number }  | { kind: "type"; text: string }  | { kind: "open"; url: string }  | { kind: "done" };type Model = (screenshot: Uint8Array) => Promise<Action>; // your computer-use modelexport async function browse(model: Model, startUrl: string, maxSteps = 30) {  await using sbx = await Sandbox.create({ diskMiB: 8192, timeoutSeconds: 1800 });  const { streamUrl } = await sbx.desktop.start({ width: 1280, height: 800 });  console.log("watch it live:", streamUrl); // private: it carries its token  await sbx.desktop.open(startUrl);  for (let step = 0; step < maxSteps; step++) {    const action = await model(await sbx.desktop.screenshot());    if (action.kind === "done") break;    if (action.kind === "click") await sbx.desktop.click(action.x, action.y);    if (action.kind === "type") await sbx.desktop.type(action.text);    if (action.kind === "open") await sbx.desktop.open(action.url);  }  return sbx.desktop.screenshot(); // the final state}
Pythonfrom withruntime import Sandboxdef browse(model, start_url: str, max_steps: int = 30) -> bytes:    """model(png_bytes) returns a dict: {"kind": "click", "x": .., "y": ..},    {"kind": "type", "text": ..}, {"kind": "open", "url": ..} or {"kind": "done"}."""    with Sandbox.create(disk_mib=8192, timeout_seconds=1800) as sbx:        desktop = sbx.desktop.start(width=1280, height=800)        print("watch it live:", desktop["streamUrl"])        sbx.desktop.open(start_url)        for _ in range(max_steps):            action = model(sbx.desktop.screenshot())            if action["kind"] == "done":                break            if action["kind"] == "click":                sbx.desktop.click(action["x"], action["y"])            elif action["kind"] == "type":                sbx.desktop.type(action["text"])            elif action["kind"] == "open":                sbx.desktop.open(action["url"])        return sbx.desktop.screenshot()

The desktop and Chromium are ready on every server. The first start in a sandbox installs nothing to its disk and takes about a second.

Three ways to drive a browser

Way What the agent sees and does Best for
The desktop Screenshots in; clicks, typing and URLs out Computer-use models; any site or app
Playwright MCP server Page structure and browser tools over MCP Agents that already speak MCP
Playwright or Puppeteer code Your script, run with exec Known flows, scraping, end-to-end tests

Start the Playwright MCP server inside the sandbox with one call. Give your agent its URL and headers:

TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await using sbx = await runtime.sandboxes.create({ diskMiB: 8192 });await sbx.mcp.start([{ id: "playwright" }]);const gateway = await sbx.mcp.ready();for (const server of gateway.servers) console.log(server.name, server.url);console.log(gateway.headers); // send these with every request

runtime.mcp.catalog() lists every server with its license and pinned version. For scripted Playwright, see Playwright in a cloud sandbox.

Watch it and keep a record

The live view is a private preview of the desktop. A person can watch the agent and step in. A recording saves the session as MP4:

TypeScriptimport { writeFile } from "node:fs/promises";import { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ diskMiB: 8192 });await sbx.desktop.start();const rec = await sbx.desktop.recordings.start({ fps: 10, maxMiB: 256 });await sbx.desktop.open("https://example.com");await sbx.desktop.recordings.stop(rec.id);await writeFile("session.mp4", await sbx.desktop.recordings.download(rec.id));
Pythonfrom withruntime import Sandboxwith Sandbox.create(disk_mib=8192) as sbx:    sbx.desktop.start()    recording = sbx.desktop.recordings.start(fps=10, max_mib=256)    sbx.desktop.open("https://example.com")    sbx.desktop.recordings.stop(recording["id"])    with open("session.mp4", "wb") as file:        file.write(sbx.desktop.recordings.download(recording["id"]))

A recording uses one encoder thread at low priority and at most 4 Mbit/s. The browser takes priority. A recording never grows past maxMiB.

Keep the agent on the sites it should visit

After the desktop and browser are running, narrow the network to the sites the task needs. The rule applies immediately, binds root inside the sandbox, and is enforced on the host:

TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ diskMiB: 8192 });await sbx.desktop.start();await sbx.desktop.open("https://example.com");await sbx.network.set({ internet: true, allow: ["example.com", "*.example.com"] });

To give the agent an API it should call, store the key as a secret. The sandbox holds only a placeholder. The proxy adds the value to HTTPS requests to the hosts you name (egress control).

Start ready

Install the desktop once. Keep that sandbox as a snapshot, and start every task from it. The copy starts with the desktop installed and running:

TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();const base = await runtime.sandboxes.create({ diskMiB: 8192 });await base.desktop.start({ width: 1280, height: 800 });await base.desktop.open("https://example.com");const snapshot = await base.snapshot({ name: "desktop-ready", retentionDays: 30 });await base.stop();await using task = await runtime.sandboxes.create({ snapshot: snapshot.id });await task.desktop.open("https://example.com");

What a browser agent needs

Need How Runtime covers it
A real browser and screen A Linux desktop with Chromium; clicks, typing, screenshots; CDP for Playwright
A person watching A private live stream link
An audit trail MP4 recordings, capped by maxMiB
One task per browser profile A Firecracker microVM per sandbox: no shared cookies, downloads or sessions
Sites the agent may visit Allow and deny lists, enforced on the host
Many sessions at the same time 100 sandboxes at the same time on a paid account
No setup per task Start from a snapshot with the desktop already running

What it costs

Assume 500 browser tasks a month. Each keeps a 2 vCPU, 4 GiB sandbox running for 3 minutes. The browser uses 0.8 of a vCPU on average:

CPU
500 × 3 min / 60 × 0.8 vCPU × $0.025
$0.50
Memory
500 × 3 min / 60 × 4 GiB × $0.0075
$0.75
Total
$1.25

Runtime charges $0.025 per vCPU-hour of measured CPU and $0.0075 per GiB-hour of memory (pricing). A 1 GB snapshot to start from adds $0.08 a month. A new account gets 100 free hours of a 1 GB machine in its first 30 days, with no card. Without credit, a sandbox reaches ports 80 and 443. Browsing needs those ports.

Start

Terminalnpx withruntime sandboxes run --keep -- echo ready

The first run prints a link. Open it in your browser to approve the run. Then runtime sandboxes desktop <id> start prints a link to watch the desktop (CLI).

Related: Playwright in a cloud sandbox, coding agent sandbox, egress control, sandbox snapshots.

Facts on this page were checked on 26 October 2026.

Related.

100 free hoursin your first 30 days.

of a 1 GB machine

  • No credit card
  • Four sandboxes at once, 2 vCPU and 4 GiB each
  • Then prepaid credit from $10, no plan fee
Start free, no card
Start free, no card