BLOG
Server Web Agent: Hosted Chromium for Production
A server web agent runs browser tasks on hosted Chromium, not a laptop. Learn how Playwright and CDP connect to a remote runtime.
# Server Web Agent: Hosted Chromium for Production
A server web agent is a browser automation process that runs on remote infrastructure instead of a developer machine or a CI runner with a local Chrome install. The agent — whether it is a Playwright script, a Puppeteer harness, or an LLM-driven loop — connects to a hosted Chromium instance over the Chrome DevTools Protocol (CDP), issues commands, and reads back page state. The browser itself lives in a data center with a stable IP, a persistent profile, and a lifecycle you control.
That distinction matters more than it sounds. Most "server web agent" problems are not agent problems. They are browser-hosting problems: the local Chrome crashed, the profile got wiped, the IP got flagged, or the session died mid-task and nobody could see why. This guide covers how the connection actually works, what to configure, and where hosted Chromium fits versus self-managed infrastructure.
What "server web agent" actually means
The phrase gets used loosely. In practice it describes three things stacked together:
- A browser process running somewhere other than your laptop — a container, a VM, or a managed session.
- A control channel — almost always CDP — that lets code drive that browser.
- An agent loop that decides what to do next: navigate, click, extract, retry.
You can build all three yourself. You can also split them: keep the agent logic wherever you like and rent only the browser. The second option is usually what people mean when they search for a server web agent, because the agent code is the part they want to own and the browser is the part they want to stop babysitting.
The control channel is the important detail. Playwright's connectOverCDP and Puppeteer's connect both speak CDP, so a hosted Chromium session is not a proprietary lock-in — it is a WebSocket endpoint that behaves like any other remote browser. If you have written Playwright against a local Chrome, you already know the API.
Why local Chrome stops working as an agent runtime
A single Playwright script on a laptop is fine. Ten concurrent agents on the same machine is where it breaks down, and the failure modes are consistent:
Resource contention. Each Chromium instance consumes a substantial amount of RAM. Run several in parallel on a 16 GB machine and you start swapping. Headless mode helps, but it does not eliminate the per-context cost.
Profile and state loss. Local runs typically start from a clean profile. That means re-authenticating on every run, re-accepting cookie banners, and losing any session the site expects you to carry. Persistent profiles solve this, but managing profile directories across machines is its own project.
IP reputation. Your office or home IP is shared with everything else you do. Sites that rate-limit or challenge automation see the same address across unrelated tasks. Residential or datacenter proxies fix this, but wiring proxies into a local Chrome launch is fiddly.
No observability. When a headless run fails at step 14, you get a stack trace and maybe a screenshot. You cannot watch the session, cannot attach a debugger mid-flight, and cannot hand the live browser to a human to finish the job.
No isolation. Two agents sharing one browser share cookies, storage, and tabs. One agent's navigation can break another's. True isolation means separate browser contexts at minimum, separate processes ideally.
A hosted runtime addresses these as infrastructure concerns rather than per-script concerns. That is the whole value proposition — not that the browser is faster, but that the operational surface shrinks.
How the connection works: CDP over WebSocket
When you launch Chromium with --remote-debugging-port, it exposes an HTTP endpoint that lists debuggable targets and a WebSocket URL per target. CDP clients connect to that WebSocket and exchange JSON messages. Playwright wraps this in connectOverCDP; Puppeteer wraps it in connect.
A hosted session gives you the same thing, just at a URL you did not have to provision. The shape is:
import { chromium, Browser, BrowserContext, Page } from 'playwright';
// The CDP endpoint comes from your hosted session.
// Treat it like any remote Chrome debugging URL.
const CDP_ENDPOINT = process.env.REMOTE_BROWSER_CDP_URL!;
async function runAgentTask(): Promise<void> {
let browser: Browser | undefined;
try {
browser = await chromium.connectOverCDP(CDP_ENDPOINT, {
timeout: 30_000,
});
// A hosted session usually starts with one context already open.
// Reuse it so cookies and storage persist across your commands.
const context: BrowserContext = browser.contexts()[0]
?? await browser.newContext({
viewport: { width: 1280, height: 800 },
locale: 'en-US',
timezoneId: 'America/New_York',
});
const page: Page = context.pages()[0] ?? await context.newPage();
await page.goto('https://example.com/dashboard', {
waitUntil: 'domcontentloaded',
timeout: 45_000,
});
// Wait for the app to settle rather than guessing with sleep().
await page.waitForLoadState('networkidle');
const heading = await page.locator('h1').first().innerText();
console.log('Loaded:', heading);
// Do agent work here: fill forms, click, extract, branch.
} finally {
// Disconnect without killing the remote browser.
// The session stays alive for the next step or a human takeover.
await browser?.close();
}
}
runAgentTask().catch((err) => {
console.error('Agent task failed:', err);
process.exitCode = 1;
});Two details in that snippet are worth calling out.
First, browser.close() on a CDP connection disconnects the client. Whether it also terminates the remote browser depends on the runtime. With a hosted session you generally want to disconnect and let the session persist, so a later step — or a human in the live viewer — can pick up where you left off. Check your runtime's semantics before relying on either behavior.
Second, reusing browser.contexts()[0] instead of calling newContext() is what preserves login state. If you create a fresh context every run, you have rebuilt the local-profile problem in the cloud.
For the protocol itself, the Chrome DevTools Protocol documentation is the authoritative reference for domains, commands, and events. Playwright's own CDP guidance is worth reading alongside it if you are deciding between connectOverCDP and launchServer.
Configuring the browser for agent workloads
Launch flags matter less on a hosted runtime than locally, because the runtime owns the process. But the settings you *can* control still shape reliability. The categories that matter:
| Setting | Local Chrome | Hosted session | Why it matters |
|---|---|---|---|
| Browser binary | You install and patch it | Managed Chromium | No version drift across workers |
| Profile persistence | Manual directory juggling | Configurable per session | Keeps auth and cookies across runs |
| Proxy / egress IP | Per-launch flag | Configurable at session level | Avoids shared-IP rate limits |
| Isolation | Separate processes you manage | Session-scoped contexts | One agent cannot poison another |
| Live viewing | Screenshots only | Live viewer | Debug and human takeover |
| Concurrency | Bounded by your RAM | Bounded by your plan | See /pricing for current limits |
| Lifecycle | You reap zombie processes | Runtime-managed | No leaked browsers after crashes |
The honest trade-off: a hosted runtime adds a network hop and a dependency. If your workload is one script that runs once a day on a machine you already own, self-hosting is cheaper and simpler. Hosted sessions start paying off when you need concurrency, persistence, or visibility — or when the cost of a failed run exceeds the cost of the browser hour.
On browser settings specifically, be careful about what you assume. Configurable viewport, locale, timezone, user agent, and proxy settings are standard. Claims about advanced anti-detection or evasion capabilities are a different category and should be evaluated against the vendor's actual documentation rather than marketing copy. For most agent workloads, consistent, well-configured sessions beat exotic evasion tricks.
Playwright, Puppeteer, and Selenium against a remote browser
The three major automation libraries all reach a remote browser, but the paths differ:
- Playwright —
chromium.connectOverCDP(endpoint)for CDP, orchromium.connect(endpoint)if the runtime exposes a Playwright server.connectOverCDPis the more portable option because it does not require the runtime to speak Playwright's protocol. - Puppeteer —
puppeteer.connect({ browserWSEndpoint }). Same CDP underneath, slightly different ergonomics. - Selenium — via
debuggerAddressin Chrome options, or through a WebDriver endpoint if the runtime provides one. Less common for agent workloads, still viable for existing test suites.
If you are migrating an existing Playwright suite, the change is usually a few lines: swap chromium.launch() for chromium.connectOverCDP(), and move any launchOptions.args you relied on into session configuration. Flags like --disable-blink-features=AutomationControlled or custom --user-agent values are launch-time concerns; on a hosted runtime they become session-level settings. If a flag has no equivalent, that is worth knowing before you migrate rather than after.
For a broader look at how remote browsers fit into agent architectures, see Remote browsers for AI agents.
Production criteria for a server web agent runtime
If you are evaluating hosted Chromium for agent work, these are the questions that separate a demo from something you can run on a schedule:
Session lifecycle. Can a session survive a client disconnect? Can you reconnect to the same session from a different process? This is what makes multi-step and human-in-the-loop workflows possible.
Profile persistence. Are profiles durable across sessions, and can you name or scope them? Persistent profiles are the difference between an agent that logs in once and one that logs in every run.
Observability. Is there a live viewer? Can you see the current page, the console, and the network activity while the agent runs? Debugging a headless failure from a stack trace alone is slow.
Isolation guarantees. Are sessions isolated at the process level or just the context level? Context isolation is usually sufficient for cookie separation; process isolation matters more for resource and crash containment.
Egress control. Can you attach a proxy per session, and does the runtime support the proxy types you need? IP quality affects success rates on sites that challenge automation.
Usage accounting. How is time metered — wall clock, active browser time, or something else? Metering model determines whether an idle session waiting on an LLM call costs you money. See /pricing for how Remote Browser meters sessions.
API surface. SDK, REST, or raw CDP? Raw CDP is the most portable; an SDK is more convenient if you are standardizing on one runtime.
None of these have a universal right answer. A batch job that runs nightly has different requirements than an interactive agent that a human supervises. Write down which ones you actually need before comparing vendors.
Where hosted Chromium fits — and where it does not
Hosted Chromium is the right call when:
- You need more concurrent browsers than one machine can host.
- Sessions must persist across process restarts or multiple workers.
- You want a live viewer for debugging or human takeover.
- You need per-session proxy control without managing proxy infrastructure.
- You are running agents on a schedule and cannot tolerate a laptop being closed.
It is the wrong call when:
- You run a single script occasionally and already have a working local setup.
- Your workload is entirely static-page scraping that
fetchhandles. - You need a browser binary you have patched yourself in ways the runtime does not support.
- Your compliance requirements forbid sending traffic through a third party.
The middle ground — self-hosted Playwright in containers — is legitimate and often the right first step. It becomes expensive when you factor in the engineering time for session management, profile storage, proxy configuration, and observability. That engineering time is what a hosted runtime is actually selling.
Getting started
The practical path from local to server-side is incremental:
- Get your agent working against local Chromium first. Do not debug two things at once.
- Move the launch call to
connectOverCDPand point it at a hosted session. - Add profile persistence so authentication survives across runs.
- Add a proxy if your target sites rate-limit by IP.
- Wire up the live viewer for the failures you cannot reproduce locally.
The documentation covers session creation, CDP endpoints, and profile configuration. If you want the conceptual background first, Remote browser online and Remote web browser cover the runtime model and the connection options in more depth.
The short version: a server web agent is a normal browser automation script pointed at a browser that is not on your machine. Everything hard about it is operational — persistence, isolation, observability, and IP quality. Pick a runtime that treats those as first-class concerns, and the agent code stays as simple as it was on your laptop.