BLOG
Puppeteer Remote Browser GitHub: Connect to Hosted Chromium
Connect Puppeteer to a remote browser from GitHub projects over CDP. Run hosted Chromium for AI agents, scraping, and tests without local Chrome.
# Puppeteer Remote Browser GitHub: Connect to Hosted Chromium
If you searched for puppeteer remote browser github, you are probably trying to do one of two things: find a GitHub project that gives you a remote Puppeteer browser, or connect your existing Puppeteer code to a hosted Chromium instance instead of launching Chrome locally. Both paths lead to the same place — a browserWSEndpoint or CDP URL that your Puppeteer script connects to over the network.
This guide covers what actually exists on GitHub, how the connection works at the protocol level, and what to check before you put a remote Puppeteer setup into production. It is written for developers running browser automation for AI agents, scraping, or test harnesses who are tired of managing Chrome binaries on every worker.
What "Puppeteer remote browser" actually means
Puppeteer has two modes. The default mode launches a local Chrome or Chromium process and talks to it over a pipe. The second mode connects to an already-running browser over the Chrome DevTools Protocol (CDP) using puppeteer.connect().
A remote browser is just the second mode pointed at a network address. Instead of puppeteer.launch(), you call:
const browser = await puppeteer.connect({
browserWSEndpoint: 'wss://your-endpoint',
});Everything after that — browser.newPage(), page.goto(), page.evaluate() — works the same way. The difference is where the Chromium process lives, who patches it, and whether it survives your script crashing.
This is the same mechanism Playwright uses with connectOverCDP, and it is documented in the Chrome DevTools Protocol spec. Puppeteer is a CDP client; a remote browser is a CDP server. That is the whole relationship.
What you find on GitHub when you search for this
Searching GitHub for "puppeteer remote browser" returns a few categories of project, and it helps to know which one you are looking at.
Self-hosted browser servers. Projects that wrap Chromium in a Docker container and expose a WebSocket endpoint. You run them yourself, usually behind a queue. They solve the "don't install Chrome on every worker" problem but not the "who patches Chromium, rotates IPs, and keeps sessions alive" problem.
Puppeteer plugins and helpers. Small libraries that add retries, stealth patches, or proxy configuration to a local Puppeteer launch. Useful, but they assume you own the browser process.
Agent frameworks. Repos like browser-use and vercel-labs/agent-browser that drive a browser from an LLM loop. These are clients, not runtimes. They need somewhere to connect.
Hosted runtime SDKs. Thin clients that hand you a connection URL for a managed Chromium session. This is the category Remote Browser sits in.
The confusion in search results comes from mixing these up. A GitHub repo that gives you a Dockerfile for headless Chrome is not the same thing as a hosted runtime with persistent profiles and a live viewer. Both are legitimate; they have different operational costs.
Connecting Puppeteer to a hosted Chromium session
The practical flow with a hosted runtime is short. You request a session, get back a CDP endpoint, and connect Puppeteer to it. Here is a TypeScript example using Playwright's CDP connect, which is the pattern most agent frameworks use today:
import { chromium } from 'playwright';
// 1. Ask the runtime for a session. The response includes a CDP URL.
const res = await fetch('https://api.remote-browser.dev/sessions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.REMOTE_BROWSER_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
profile: 'checkout-agent', // persistent profile name
timeoutSeconds: 900,
}),
});
const { cdpUrl, sessionId } = await res.json();
// 2. Connect over CDP. No local Chrome binary required.
const browser = await chromium.connectOverCDP(cdpUrl);
const context = browser.contexts()[0] ?? await browser.newContext();
const page = context.pages()[0] ?? await context.newPage();
await page.goto('https://example.com/dashboard');
const title = await page.title();
console.log({ sessionId, title });
// 3. Disconnect without killing the remote browser if you want to reattach later.
await browser.close();The Puppeteer equivalent swaps chromium.connectOverCDP for puppeteer.connect({ browserWSEndpoint }). The endpoint format differs slightly between providers, so read the connection docs rather than guessing the scheme.
Two details matter more than they look. First, browser.close() on a CDP connection may terminate the remote session depending on the runtime — check whether you want disconnect() semantics instead. Second, persistent profiles are what let a session keep cookies and localStorage across runs, which is the difference between an agent that logs in once and one that logs in every time.
Local Puppeteer vs remote Puppeteer: the real trade-offs
The comparison that matters is not "which is faster" but "which fails less often in production."
| Dimension | Local Puppeteer | Remote hosted Chromium |
|---|---|---|
| Chrome install | Per worker, per version | Managed by the runtime |
| Version drift | You pin and patch | Runtime pins and patches |
| Session persistence | Manual, filesystem-bound | Profile-backed, reattachable |
| IP / network | Your egress IP | Configurable proxy settings |
| Live debugging | Screenshots, logs | Live viewer + CDP |
| Scaling model | One Chrome per worker | Session-per-task, pooled |
| Failure recovery | Process dies, state lost | Session survives worker restart |
| Cost model | Compute you already pay for | Metered browser time |
Local Puppeteer wins on latency and cost when you run a handful of short tasks on a machine you already own. Remote wins when you run many concurrent sessions, need consistent browser versions, or want to debug a live session without SSHing into a worker.
The failure mode people underestimate is version drift. A local setup where three workers have three different Chrome builds produces bugs that only reproduce on one of them. A hosted runtime gives every session the same build.
Production criteria before you commit
Before you point production traffic at any remote browser — GitHub-hosted or commercial — check these:
- CDP compatibility. Does it expose a standard CDP endpoint, or a proprietary protocol? Standard CDP means Puppeteer, Playwright, and Selenium all work without adapters.
- Session lifecycle. Can you reattach to a session after your worker restarts? Persistent profiles and session IDs make this possible.
- Isolation. Are sessions isolated from each other at the process or container level? Shared browsers leak state between tasks.
- Network controls. Can you configure proxy settings and region? This matters for geo-restricted content and for keeping agent traffic off your own IPs.
- Observability. Is there a live viewer, or only logs? Being able to watch a session in real time cuts debugging time dramatically.
- Usage controls. Do you get per-session time limits and spend caps? Unbounded browser time is how automation bills surprise you.
- CAPTCHA and bot handling. Some runtimes ship hardened Chromium and CAPTCHA handling; others leave it to you. Know which you are buying.
For a deeper look at how these criteria map to agent workloads, see Remote browsers for AI agents.
Where Puppeteer fits in an AI agent stack
Puppeteer is a low-level driver. It gives you a page object and CDP access; it does not decide what to click. In an agent stack, the layers look like this:
- Model layer — decides the next action from page state.
- Agent framework — translates decisions into tool calls (
click,type,extract). - Driver — Puppeteer, Playwright, or Selenium executes the calls.
- Runtime — the remote browser hosting Chromium, profiles, and network.
Most "puppeteer remote browser github" searches are really about layer 4. The framework and driver are already chosen; the missing piece is a runtime that does not require you to run Chrome infrastructure.
This is also why CDP matters for agents. A CDP endpoint lets an agent framework attach to a session, inspect the DOM, and act — without the framework caring whether the browser is local or 500 miles away. If you want the connection-level detail, Remote Browser CDP covers the handshake and session semantics.
Common mistakes when wiring Puppeteer to a remote browser
Hardcoding the endpoint. Session URLs are usually short-lived. Fetch a fresh one per task instead of caching it in a config file.
Ignoring disconnect vs close. browser.close() may kill the remote session. If you want to reattach, use the disconnect path your client provides.
Skipping profile cleanup. Persistent profiles accumulate cookies and storage. Without a cleanup policy, agents inherit stale auth state and behave unpredictably.
Assuming no concurrency limits. Every runtime has session limits. Read the current limits on /pricing rather than assuming you can fan out to many parallel sessions at once.
Not testing reconnection. The whole point of a remote browser is surviving worker restarts. Test that path before you rely on it.
Treating stealth as a checkbox. Configurable browser settings help, but no runtime guarantees you will pass every bot check. Plan for detection and retries rather than assuming immunity.
Choosing between a GitHub project and a hosted runtime
If you want full control and have the ops capacity, a self-hosted Chromium server from GitHub is a reasonable starting point. You get transparency and no per-hour cost, and you own the failure modes.
If your bottleneck is operational — patching Chrome, managing profiles, debugging sessions across workers, handling proxies — a hosted runtime removes that work. The trade-off is a metered cost and a dependency on someone else's uptime.
A pragmatic middle path: prototype locally with Puppeteer, then move to a hosted runtime when you hit the first of these — more than a few concurrent sessions, a need for persistent profiles, or a debugging session that requires watching the browser live. For a walkthrough of running Chromium without local setup, see Remote browser online.
Getting started
The fastest path is to keep your Puppeteer or Playwright code and change only the connection step. Replace launch() with a connect call against a session endpoint, keep your page logic identical, and verify that reconnection and profile persistence behave the way you expect.
If you want the full connection reference — endpoints, session lifecycle, and profile handling — start with the documentation. Current session limits and metering are on /pricing. And if you are building an agent rather than a scraper, Remote web browser explains how the runtime layer fits under an LLM loop.
The short version: Puppeteer does not care whether Chromium is local or remote, as long as it speaks CDP. The work is in choosing a runtime that survives production — and in testing the reconnection path before your first real workload depends on it.