The browser template provides a cloud-native browser runtime. It lets you remotely control a browser instance running in an isolated cloud container over the standard Chrome DevTools Protocol (CDP) over WebSocket, with native compatibility for automation frameworks such as Puppeteer and Playwright.
The browser template is not a ready-to-use built-in template: you must first build a named custom template from the official browser image, then create sandboxes from it. This page covers only capabilities, default configuration, build and minimal verification, and endpoint differences. For the full framework-integration tutorials (BrowserUse, LangChain, and more), see Use Browser Use Sandbox.
Features
| Feature | Description |
| Browser automation | Ships with Chromium/Chrome, supports full web standards, natively compatible with Puppeteer, Playwright, and other automation frameworks |
| CDP remote control | Precisely drive dynamically rendered pages (SPAs) over the standard CDP protocol over WebSocket, while reliably maintaining login state and sessions |
| Real-time VNC | Built-in VNC service lets you view the browser desktop in real time through a noVNC client, making debugging and monitoring easy |
| Secure isolation | Each browser sandbox instance has its own file system and process space |
| Encrypted transport | All data-plane endpoints (CDP and VNC) use the WSS (WebSocket Secure) protocol, encrypted end to end |
Default configuration
| Item | Default | Description |
| Container image | fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/browser:v0.0.44 | Prebuilt browser image |
| Default port | 3000 | Port the sandbox service listens on |
| CPU | 4 vCPU | Recommended starting specification |
| Memory | 8192 MB | Recommended starting specification |
| Disk size | 10240 MB | 10 GB is recommended for sufficient temporary storage |
Build and minimal verification
Usage has two phases: first build the template (materialize a named template from the browser image), then run the template (create a sandbox, wait for the health check, open a page over CDP, and take a screenshot).
The example uses the Beijing image. For another region, replace the region segment in the image address with the FC Agent Sandbox endpoint region and keep the v0.0.44 tag. Set E2B_API_KEY, E2B_API_URL, and E2B_DOMAIN (through .env or export) to that same region. The SDK reads them automatically, so you do not need to pass them into method calls. When you connect to the CDP/VNC endpoints, include the X-Access-Token header for authentication: the Python SDK exposes it through the internal attribute sbx._envd_access_token (which may be renamed or removed in future versions), and the JS SDK exposes sbx.envdAccessToken (declared protected in TypeScript, but readable at runtime).
Prepare the local environment
Python:
uv venv .venv --python 3.12
source .venv/bin/activate
uv pip install e2b==2.31.0 e2b-code-interpreter==2.8.1 'playwright>=1.49.0'
playwright install chromiumNode.js: use the following package.json, then run npm install.
{
"name": "browser-template-demo",
"version": "1.0.0",
"type": "module",
"dependencies": {
"@e2b/code-interpreter": "^2.6.1",
"e2b": "^2.31.0",
"playwright-core": "^1.49.0"
}
}Build the browser template
Build a named template from the browser image, specifying CPU and memory (4 vCPU / 8192 MB recommended) at build time.
Python:
"""Browser template build example."""
import os
from dotenv import load_dotenv
from e2b import Template, default_build_logger
load_dotenv()
# The SDK reads E2B_API_KEY / E2B_API_URL / E2B_DOMAIN automatically.
FROM_IMAGE = "fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/browser:v0.0.44"
build = Template.build(
Template().from_image(FROM_IMAGE),
name="my-browser-template",
cpu_count=4,
memory_mb=8192,
on_build_logs=default_build_logger(),
)
print(f"template_id: {build.template_id}")Node.js:
// Browser template build example.
import { Template, defaultBuildLogger } from 'e2b';
// The SDK reads E2B_API_KEY / E2B_API_URL / E2B_DOMAIN automatically.
const FROM_IMAGE =
'fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/browser:v0.0.44';
const build = await Template.build(Template().fromImage(FROM_IMAGE), 'my-browser-template', {
cpuCount: 4,
memoryMB: 8192,
onBuildLogs: defaultBuildLogger(),
});
console.log(`template_id: ${build.templateId}`);Run the template and verify
After creating the sandbox, poll /health until the browser service is ready, then connect Playwright over the CDP endpoint to open the target page and take a screenshot.
Python:
"""Browser template run example: create sandbox -> wait for health check -> CDP automation -> screenshot."""
import time
from e2b_code_interpreter import Sandbox
from playwright.sync_api import sync_playwright
# The SDK reads E2B_API_KEY / E2B_API_URL / E2B_DOMAIN automatically.
BROWSER_PORT = 3000
def wait_until_healthy(sbx: Sandbox, host: str, token: str, timeout: int = 60) -> None:
"""Poll the public gateway /health endpoint until the browser service is ready or times out."""
token_header = f"-H 'X-Access-Token: {token}' " if token else ""
deadline = time.time() + timeout
while time.time() < deadline:
result = sbx.commands.run(
f"curl -sS -o /dev/null -w '%{{http_code}}' -m 4 {token_header}https://{host}/health",
timeout=10,
)
if "".join(result.stdout or []).strip() == "200":
return
time.sleep(2)
raise TimeoutError(f"browser service not ready within {timeout}s")
sbx = None
try:
sbx = Sandbox.create(template="my-browser-template", timeout=900)
host = sbx.get_host(BROWSER_PORT)
token = sbx._envd_access_token # the public gateway requires X-Access-Token, otherwise 403
headers = {"X-Access-Token": token} if token else {}
wait_until_healthy(sbx, host, token)
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(f"wss://{host}/ws/automation", headers=headers)
context = browser.contexts[0] if browser.contexts else browser.new_context()
page = context.new_page()
page.goto("https://example.com", wait_until="domcontentloaded", timeout=30000)
print(f"page.title() = {page.title()!r}")
page.screenshot(path="browser-example.png", full_page=True)
browser.close()
finally:
if sbx is not None:
sbx.kill()Node.js:
// Browser template run example: create sandbox -> wait for health check -> CDP automation -> screenshot.
import { Sandbox } from '@e2b/code-interpreter';
import { chromium } from 'playwright-core';
// The SDK reads E2B_API_KEY / E2B_API_URL / E2B_DOMAIN automatically.
const BROWSER_PORT = 3000;
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
/** Poll the public gateway /health endpoint until the browser service is ready or times out. */
async function waitUntilHealthy(sbx, host, token, timeout = 60) {
const tokenHeader = token ? `-H 'X-Access-Token: ${token}' ` : '';
const deadline = Date.now() + timeout * 1000;
while (Date.now() < deadline) {
let result;
try {
result = await sbx.commands.run(
`curl -sS -o /dev/null -w '%{http_code}' -m 4 ${tokenHeader}https://${host}/health`,
{ timeoutMs: 10_000 },
);
} catch (e) {
result = e; // while the service is down curl exits non-zero; the exception still carries stdout
}
if ((result.stdout || '').trim() === '200') return;
await sleep(2000);
}
throw new Error(`browser service not ready within ${timeout}s`);
}
let sbx = null;
try {
sbx = await Sandbox.create('my-browser-template', { timeoutMs: 900_000 });
const host = sbx.getHost(BROWSER_PORT);
const token = sbx.envdAccessToken; // the public gateway requires X-Access-Token, otherwise 403
const headers = token ? { 'X-Access-Token': token } : {};
await waitUntilHealthy(sbx, host, token);
const browser = await chromium.connectOverCDP(`wss://${host}/ws/automation`, { headers });
const contexts = browser.contexts();
const context = contexts.length ? contexts[0] : await browser.newContext();
const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded', timeout: 30_000 });
console.log(`page.title() = ${JSON.stringify(await page.title())}`);
await page.screenshot({ path: 'browser-example.png', fullPage: true });
await browser.close();
} finally {
if (sbx !== null) await sbx.kill();
}WebSocket endpoints
The browser template exposes the following endpoints through port 3000. All require the X-Access-Token header for authentication, and <sandbox-host> is obtained through the SDK's sbx.get_host(3000):
| Endpoint | Path | Purpose |
| Health check | https://<sandbox-host>/health | Determine whether the browser service has finished starting |
| CDP automation | wss://<sandbox-host>/ws/automation | Browser automation, compatible with Puppeteer and Playwright |
| VNC livestream | wss://<sandbox-host>/ws/livestream | View the browser desktop in real time, viewable through a noVNC client |
Inside the sandbox you can first probe whether the CDP WebSocket handshake works. A 101 Switching Protocols response means the endpoint can be upgraded to a WebSocket connection:
curl -sS -m 4 -i \
-H 'Connection: Upgrade' -H 'Upgrade: websocket' \
-H 'Sec-WebSocket-Version: 13' -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' \
http://localhost:3000/ws/automationAfter returning 101 Switching Protocols, the server continues sending WebSocket frames. curl may wait until -m 4 expires and exit with code 28. This does not indicate a failed handshake; check whether the response contains 101 Switching Protocols.
The browser WebSocket API cannot set custom headers during the handshake, so pure-browser clients such as noVNC cannot send X-Access-Token and will receive 403 on a direct connection. Use a WebSocket client that supports custom headers (such as wscat or Python websockets) to send X-Access-Token and complete the RFB handshake. If you only need to view the result, connect over CDP and call page.screenshot().
Window and screen size
Since browser image v0.0.37, the browser window and virtual screen size are controlled by three environment variables in the image:
| Environment variable | Purpose | Default |
RESOLUTION | Xvfb virtual screen resolution (WxHxdepth) | 1680x1050x24 |
BROWSER_WINDOW_SIZE | Chrome startup window size (--window-size) | Width and height from RESOLUTION |
VNC_CLIP | Clipping area of the VNC live stream | Same as the window size |
The envs parameter of Sandbox.create is only injected into command-execution processes inside the sandbox and does not affect the browser stack. To change the window size, bake the environment variables into a custom image with a Dockerfile, push it to an image registry, and then build the template with from_image:
FROM fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/browser:v0.0.44
ENV RESOLUTION=1920x1080x24
ENV BROWSER_WINDOW_SIZE=1920x1080
ENV VNC_CLIP=1920x1080Then replace FROM_IMAGE with this custom image address when building the template; for other regions, replace the region in the image address with your sandbox region. The window size also determines the VNC live-stream area; the in-page viewport is still set by the client (Puppeteer, Playwright, and so on) over CDP.
Limitations
| Item | Constraint |
| Browser support | Currently ships with Chromium/Chrome |