Skip to content

Instantly share code, notes, and snippets.

@sachinraja
Last active July 18, 2025 22:55
Show Gist options
  • Save sachinraja/38d2f3dc0295489c415838fd394845a2 to your computer and use it in GitHub Desktop.
Save sachinraja/38d2f3dc0295489c415838fd394845a2 to your computer and use it in GitHub Desktop.
browser-in-sandbox
import { Sandbox } from "@vercel/sandbox";
import z from "zod";
import { retryWithBackoff } from "./utils";
import { chromium } from 'playwright'
const cdpVersionSchema = z.object({
webSocketDebuggerUrl: z.string(),
});
const BROWSERS_DIR = "browsers";
const PORT = 9222;
async function setupSandbox(sandbox: Sandbox) {
await sandbox.mkDir(BROWSERS_DIR);
const chromeRpmPath = `${BROWSERS_DIR}/google-chrome-stable_current_x86_64.rpm`;
const downloadRpm = await sandbox.runCommand({
cmd: "curl",
args: [
"https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm",
"--output",
chromeRpmPath,
],
});
if (downloadRpm.exitCode !== 0) {
console.error("Failed to download Chrome RPM:", downloadRpm.stderr);
throw new Error("Chrome RPM download failed");
}
const installChrome = await sandbox.runCommand({
cmd: "dnf",
args: ["install", "-y", chromeRpmPath],
sudo: true,
});
if (installChrome.exitCode !== 0) {
console.error("Failed to install Chrome:", installChrome.stderr);
throw new Error("Chrome installation failed");
}
await sandbox.runCommand({
cmd: "google-chrome",
args: [
"--headless",
"--no-sandbox",
`--remote-debugging-port=${PORT}`,
"--remote-debugging-address=0.0.0.0",
],
detached: true,
});
// health check until the browser is ready
const fetchVersion = await retryWithBackoff(async () => {
const result = await sandbox.runCommand({
cmd: "curl",
args: [`-s`, `http://localhost:${PORT}/json/version`],
});
if (result.exitCode !== 0) {
throw new Error("Failed to fetch version");
}
return result;
});
const versionOutput = await fetchVersion.output();
const data = JSON.parse(versionOutput);
const { webSocketDebuggerUrl } = cdpVersionSchema.parse(data);
const url = new URL(webSocketDebuggerUrl);
const sandboxUrl = new URL(sandbox.domain(PORT));
const externalUrl = `wss://${sandboxUrl.host}${url.pathname}`;
return externalUrl;
}
export async function createBrowserSandbox() {
const sandbox = await Sandbox.create({
timeout: 1000 * 60 * 5, // 5 minutes
ports: [PORT],
runtime: "node22",
});
try {
const webSocketDebuggerUrl = await setupSandbox(sandbox);
return { sandbox, webSocketDebuggerUrl };
} catch (e) {
await sandbox.stop();
throw e;
}
}
async function main() {
const { sandbox, webSocketDebuggerUrl } = await createBrowserSandbox()
const browser = await chromium.connectOverCDP(webSocketDebuggerUrl)
// do stuff...
await sandbox.stop()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment