DEV Community

RC
RC

Posted on Originally published at randomchaos.us

Firecracker contains the attack it never sees

Every browser session on Browser Use Cloud runs inside its own Firecracker microVM, and that VM cold-starts in under 400ms. The isolation is the whole point. A cloud browser carries Chromium, a filesystem, cookies, cache, proxy settings, downloads, and sometimes a logged-in customer session. If one session can read another's state, that's a breach. A VM gives each browser its own CPU, memory, disk, and network devices, kept separate from the host and from every other VM. When a browser breaks, leaks, or gets compromised, the damage stays inside the VM. Firecracker doesn't inspect the browser or classify the attack. It holds the boundary regardless of what hits it.

The rebuild brought a new session to under a second and dropped the price to $0.02 per browser hour, down from $0.06.

VMs inside VMs

The unusual choice is where Firecracker runs. The normal pattern is a .metal instance, where you rent the whole physical server and Firecracker runs directly on it. Browser Use runs it on regular EC2, which AWS has already placed inside its own isolation layer. Every browser is therefore a VM inside a VM. On paper that is slow: when a browser VM needs the host, the request crosses two virtualization layers instead of one.

Regular EC2 is faster to acquire and cheaper to keep around. Hosts boot from a pre-built image and start serving browsers about 30 seconds after launch, so less idle capacity sits waiting, and the saving passes to customers.

The earlier infrastructure ran on Unikraft, which handled browser standby, wake-up, and scale-to-zero well. The gap was at the fleet layer: native horizontal EC2 autoscaling wasn't available in that setup, so provisioning new nodes was manual and capacity could fall behind demand. Building on Firecracker meant writing a control plane to decide how many VMs to run, where to place them, and when to add more. It checks the fleet in real time rather than waiting on CloudWatch's one-minute windows, and it tracks state generic metrics miss: browsers still starting, and machines being drained so they stop receiving new sessions.

Making nested virtualization fast

Browsers resume from a snapshot, a VM already booted and paused just before Chromium launches. The first problem was memory. When a resumed VM touches restored memory, the host has to map it back in, a page fault, and under nesting each fault is expensive. During early cold starts, page faults were 72% of all VM exits, and getting from resume to a CDP-ready browser took 9.8 seconds.

Two changes fixed it. Memory now restores in 2MB pages instead of 4KB, so each page covers 512 times more memory and the browser triggers far fewer faults while waking. A custom userfaultfd handler preloads the pages Chromium reads first, before the VM runs, so the hot set is already resident. Resume-to-ready fell from 9.8 to 3.1 seconds, and page faults per resume dropped from roughly 100,000 to about 1,100, a 91x reduction. Two smaller wins: a phantom PS/2 keyboard probe that cost 500ms was disabled, and HTTP readiness polling was replaced by a ready message the browser driver writes to a log the host reads over vsock in under a millisecond.

Chromium's startup burst was the next bottleneck. It spins up renderers, compositors, and V8 isolates all at once, then goes quiet. During the burst, vCPUs stay unpinned so Linux can spread the work across the host; once the browser reports ready, they're pinned to stable cores for dense packing. Pinning from the start made it worse, piling launches onto the same hot cores until some failed. Each browser now also gets both hyperthread siblings of its physical core so two VMs don't contend for one core, and pinned vCPU threads get real-time priority. A 1,000-browser test lost 17% of sessions without that priority and zero with it.

Headless and still hard to block

Stealth normally forces a tradeoff. Plain headless Chromium passed Browser Use's stealth benchmark 2% of the time; the same build headful, rendering to a window nobody watches, passed 50%. Most providers pay for the display server, GPU, and compositor to get that number. Browser Use stays fully headless by patching Chromium at its lowest level instead of injecting JavaScript, which websites detect when flags like navigator.webdriver are overwritten, and by drawing on tens of thousands of real fingerprints across macOS, Windows, and Linux. They report 81% on their own benchmark and 84.8% on Halluminate BrowserBench.

End to end, create latency is 825ms at p50 and 1.35s at p99, measured across a 10,000-session stress test in which every browser started. The security model travels even if the latency work doesn't: one microVM per untrusted workload means you never have to trust Chromium's sandbox, a renderer, or an extension to hold.

Chromium startup is now the largest remaining cost at about 545ms at p50. The next step is to snapshot later, after Chromium is already running, so a new session wakes with the browser alive. That means freezing open devices, timers, graphics, and network state into a safe state, then restoring each as its own browser rather than a clone of the last one.

Top comments (0)