Advanced
Cloud Browsers
No screen locally, no Chrome, not even an always-on server? chrome-use runs anyway. There are two proven ways to move the browser into the cloud: Vercel Sandbox's on-demand microVMs, and AWS Bedrock AgentCore's managed cloud browser sessions. The command-line usage is almost identical to what you type locally — the only difference is which machine the browser runs on.
Vercel Sandbox: on-demand microVMs
Vercel Sandbox spins up a Linux microVM on demand, runs your browser commands inside it, and destroys it when you're done. It works with any framework deployed on Vercel (Next.js, SvelteKit, Nuxt, Remix, Astro, and more), and it's not subject to the serverless binary size limit — because Chromium lives inside the VM, not bundled into your function.
Install dependencies
First, add the Sandbox SDK to your project:
$ pnpm add @vercel/sandbox
Inside the VM you need Chromium's system libraries, chrome-use itself, and the Chromium browser. On the
first run, install the system libraries via dnf (Amazon Linux), install chrome-use with npm,
and finally run chrome-use install to fetch the browser:
import { Sandbox } from "@vercel/sandbox";
// System libraries Chromium needs on the sandbox VM (Amazon Linux / dnf)
const CHROMIUM_SYSTEM_DEPS = [
"nss", "nspr", "libxkbcommon", "atk", "at-spi2-atk", "at-spi2-core",
"libXcomposite", "libXdamage", "libXrandr", "libXfixes", "libXcursor",
"libXi", "libXtst", "libXScrnSaver", "libXext", "mesa-libgbm", "libdrm",
"mesa-libGL", "mesa-libEGL", "cups-libs", "alsa-lib", "pango", "cairo",
"gtk3", "dbus-libs",
];
async function withBrowser<T>(
fn: (sandbox: InstanceType<typeof Sandbox>) => Promise<T>,
): Promise<T> {
const snapshotId = process.env.AGENT_BROWSER_SNAPSHOT_ID;
const sandbox = snapshotId
? await Sandbox.create({ source: { type: "snapshot", snapshotId }, timeout: 120_000 })
: await Sandbox.create({ runtime: "node24", timeout: 120_000 });
if (!snapshotId) {
await sandbox.runCommand("sh", ["-c",
`sudo dnf install -y --skip-broken ${CHROMIUM_SYSTEM_DEPS.join(" ")} && sudo ldconfig`]);
await sandbox.runCommand("npm", ["install", "-g", "chrome-use"]);
await sandbox.runCommand("npx", ["chrome-use", "install"]);
}
try { return await fn(sandbox); }
finally { await sandbox.stop(); }
}
chrome-use open, chrome-use snapshot,
chrome-use click… no different from local. The sandbox just ferries those commands into the microVM
to execute.
Screenshots and snapshots
screenshot --json saves the image to a file and returns its path; read that file back as base64
to hand it to your frontend. To read the page structure, use snapshot -i -c (interactive elements +
compact output). The sandbox stays alive across multiple commands, so you can chain a complete flow:
return withBrowser(async (sandbox) => {
await sandbox.runCommand("chrome-use", ["open", url]);
// Get the title
const t = await sandbox.runCommand("chrome-use", ["get", "title", "--json"]);
const title = JSON.parse(await t.stdout())?.data?.title || url;
// Screenshot → read file → base64
const ss = await sandbox.runCommand("chrome-use", ["screenshot", "--json"]);
const ssPath = JSON.parse(await ss.stdout())?.data?.path;
const b64 = await sandbox.runCommand("base64", ["-w", "0", ssPath]);
await sandbox.runCommand("chrome-use", ["close"]);
return { title, screenshot: (await b64.stdout()).trim() };
});
Multi-step form flows
Because the sandbox persists across commands, a full automation — fill → submit → wait for load → screenshot —
runs in one go. A typical sequence is open → snapshot -i to get element
@refs → fill each one → click → wait --load networkidle:
await sandbox.runCommand("chrome-use", ["open", url]);
const snap = await sandbox.runCommand("chrome-use", ["snapshot", "-i"]);
// Parse the snapshot to get each field's ref…
for (const [ref, value] of Object.entries(data)) {
await sandbox.runCommand("chrome-use", ["fill", ref, value]);
}
await sandbox.runCommand("chrome-use", ["click", "@e5"]);
await sandbox.runCommand("chrome-use", ["wait", "--load", "networkidle"]);
Sandbox snapshots: sub-second cold starts
A sandbox snapshot is a VM image with the system libraries + chrome-use + Chromium already installed, much like a Docker image: on boot it starts straight from the image instead of installing everything from scratch each time. Without a snapshot, every run installs dependencies + chrome-use + Chromium (about 30 seconds); with a snapshot, startup drops into sub-second territory.
chrome-use snapshot, which exports the page's a11y tree).
Create a snapshot once, then store the returned ID in an environment variable:
# The repo demo ships a ready-made creation script
$ npx tsx examples/environments/scripts/create-snapshot.ts
# The script returns a snapshotId; write it into an env var
$ AGENT_BROWSER_SNAPSHOT_ID=snap_xxxxxxxxxxxx
Authentication
When deployed on Vercel, the Sandbox SDK authenticates automatically via OIDC — no extra configuration needed.
For local development or when you need explicit control, set the following three variables; they're spread into
Sandbox.create(), and when absent the SDK falls back to the auto-injected
VERCEL_OIDC_TOKEN:
VERCEL_TOKEN=<personal-access-token>
VERCEL_TEAM_ID=<team-id>
VERCEL_PROJECT_ID=<project-id>
Scheduled jobs (Cron)
Wire withBrowser into Vercel Cron Jobs to run periodic browser tasks — for example, scrape a
pricing page every morning at 9 and compare it for alerts:
{ "crons": [{ "path": "/api/cron", "schedule": "0 9 * * *" }] }
Vercel environment variables at a glance
| Variable | Required | Description |
|---|---|---|
AGENT_BROWSER_SNAPSHOT_ID | No (recommended) | Pre-built sandbox snapshot ID, for sub-second startup |
VERCEL_TOKEN | No | Vercel personal access token (for local dev; automatic via OIDC on Vercel) |
VERCEL_TEAM_ID | No | Vercel team ID (for local dev) |
VERCEL_PROJECT_ID | No | Vercel project ID (for local dev) |
The same pattern is written identically across frameworks — only the location of the server-side code differs:
Next.js uses server actions / route handlers, SvelteKit uses +page.server.ts / +server.ts,
Nuxt uses server/api/, Remix uses loader / action, and Astro uses
.astro frontmatter or API routes.
A full runnable example lives upstream in agent-browser's examples/environments/ (not vendored here; see Lineage & Upstream).
AWS Bedrock AgentCore: managed cloud browser
AgentCore runs the browser session as a managed service on AWS, and the usage of every chrome-use command is
completely unchanged — the only difference is that the browser runs on AWS. Just add a -p agentcore
and everything else stays the same:
# Open a page on the AgentCore cloud browser
$ chrome-use -p agentcore open https://example.com
# From here on, exactly like local Chrome
$ chrome-use snapshot -i
$ chrome-use click @e1
$ chrome-use screenshot page.png
$ chrome-use close
Credential resolution
AWS credentials are resolved automatically; when usable credentials already exist, no extra configuration is needed. The resolution order is:
- Environment variables
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY(optionallyAWS_SESSION_TOKEN) - AWS CLI fallback (
aws configure export-credentials), supporting SSO, IAM roles, and named profiles
# Explicit credentials (CI/CD, scripts)
$ export AWS_ACCESS_KEY_ID=AKIA...
$ export AWS_SECRET_ACCESS_KEY=...
$ chrome-use -p agentcore open https://example.com
# SSO (interactive)
$ aws sso login --profile my-profile
$ AWS_PROFILE=my-profile chrome-use -p agentcore open https://example.com
# IAM role / default credential chain
$ chrome-use -p agentcore open https://example.com
Persistent profiles: keep the login
Use AGENTCORE_PROFILE_ID to persist browser state (cookies, localStorage) across sessions —
perfect for keeping a login alive: run the login flow once, and every run after that is already logged in.
For a fuller take on login and session reuse, see Sessions and
Login & auth.
# First time: log in
$ AGENTCORE_PROFILE_ID=my-app chrome-use -p agentcore open https://app.example.com/login
$ chrome-use snapshot -i
$ chrome-use fill @e1 "user@example.com"
$ chrome-use fill @e2 "password"
$ chrome-use click @e3
$ chrome-use close
# Later: already logged in
$ AGENTCORE_PROFILE_ID=my-app chrome-use -p agentcore open https://app.example.com/dashboard
Live View: watch it in the console in real time
When a session starts, AgentCore prints a Live View URL to stderr. Open it in a browser and you can watch this session's screen live from the AWS console — especially handy when debugging headless flows:
Session: abc123-def456
Live View: https://us-east-1.console.aws.amazon.com/bedrock-agentcore/browser/aws.browser.v1/session/abc123-def456#
Pick a region / set the provider via env var
The default region is us-east-1; switch it explicitly with AGENTCORE_REGION.
If you don't want to add -p agentcore on every command, set AGENT_BROWSER_PROVIDER:
# Explicit region
$ AGENTCORE_REGION=eu-west-1 chrome-use -p agentcore open https://example.com
# Pin the provider via env var so commands no longer need -p
$ export AGENT_BROWSER_PROVIDER=agentcore
$ export AGENTCORE_REGION=us-east-2
$ chrome-use open https://example.com
$ chrome-use snapshot -i
$ chrome-use close
AgentCore environment variables at a glance
| Variable | Description | Default |
|---|---|---|
AGENTCORE_REGION | AWS region | us-east-1 |
AGENTCORE_BROWSER_ID | Browser identifier | aws.browser.v1 |
AGENTCORE_PROFILE_ID | Persistent browser profile (cookies, localStorage) | (none) |
AGENTCORE_SESSION_TIMEOUT | Session timeout (seconds) | 3600 |
AWS_PROFILE | AWS CLI profile used for credential resolution | default |
Common issues
- Failed to run aws CLI: the AWS CLI isn't installed or isn't on PATH. Install it, or just set
AWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEY. - AWS CLI failed: ... Run 'aws sso login': SSO credentials expired; run
aws sso loginonce to refresh. - Session timeout: the default is 3600 seconds (1 hour). For long tasks, extend it with
AGENTCORE_SESSION_TIMEOUT=7200.
How to choose between the two paths
Choose it when you already deploy your app on Vercel, want on-demand microVMs that spin up and tear down, and don't want to be blocked by the serverless binary size limit. Use a sandbox snapshot for second-scale cold starts.
Choose it when you live in the AWS ecosystem, want a managed cloud browser session, need a persistent profile to keep a login alive, or want to watch a session live in the console via Live View. The command-line experience is no different from local.