shiichan

Browser Run moved to Cloudflare Containers and now scales 4x!

Hey everyone, it's Shiichan! Today's news is one of my favorites -- the "faster and more scalable" kind. Cloudflare rebuilt Browser Run from the ground up, and it got a big power-up. Let's dive in!

Cloudflare Blog blog.cloudflare.com

What was announced?

On the Cloudflare Blog, the team announced they rebuilt Browser Run on top of Cloudflare Containers. You can now spin up 60 browsers per minute through the Workers binding and run up to 120 at once -- 4x the previous limit. On top of that, Quick Action response times dropped by more than 50%. Best of all, existing users don't need to change a thing. It's live today.

New to it? Browser Run lets you programmatically control headless browsers running on Cloudflare's global network. Think end-to-end testing, investigating suspicious URLs, rendering PDFs, taking screenshots, and extracting content. Lately it has also become a key building block for AI agents that browse the web.

The story so far

Browser Run used to share infrastructure with Browser Isolation (BISO). They're technically similar, but BISO's larger container images made startup slow, its browsers weren't distributed globally in an optimal way (hurting latency and resiliency), and BISO's long, steady sessions clashed with Browser Run's short, spiky traffic -- creating scaling bottlenecks.

Then DO-enabled Containers landed in open beta. True to the "Customer Zero" spirit of running on their own platform first, the team moved Browser Run onto dedicated infrastructure.

What changes

The migration was gradual. First they slipped a Worker into the request path to serve both BISO and Container browsers to a small group and compare them. Then they rolled Container browsers out to all Quick Actions endpoints, then free accounts, then pay-as-you-go, and finally contract customers -- with no redeploys or config changes needed on your side.

And because Browser Run now has dedicated infrastructure, the team can ship fixes and new features faster, so the product you use keeps getting better.

Dive Deep

Here's the fun part -- the team cleared two big hurdles.

Hurdle 1: distance and latency. DO-enabled Containers create a Durable Object close to the request, but the connected Container can spin up on the other side of the world. That's fine for one-shot messages, but a single screenshot means dozens of WebSocket round trips, and those milliseconds add up. The fix: pre-warmed regional pools of DO-backed browser containers that cap the distance between a DO and its container. When a request arrives, they pick the closest DO-container pair within that region.

Hurdle 2: state management. At first each container's state lived in Workers KV, but KV's eventual consistency of around 30 seconds became a bottleneck (KV recently lowered its minimum cache TTL to 30 seconds, but that's still too long). A container that looks "available" might already be claimed by the time you route to it -- hello, race conditions.

So they moved the state into D1. D1's transactions are a great fit: once a browser is assigned to a user it's exclusively theirs, and SQLite transactions stop two requests from grabbing the same browser at once. Here's a simplified acquisition query:

WITH candidate_pool AS (
    -- candidate pool logic to pick based on latency and other rules
)
UPDATE containers
SET status = 'picked'
WHERE sessionId IN (
    SELECT sessionId
    FROM candidate_pool
    ORDER BY RANDOM()
    LIMIT ?5
)
RETURNING data

But then D1 write throughput became the wall. With thousands of containers updating every 5 seconds, 1 ms writes cap you at about 1,000 writes per second -- just 5,000 containers before the database is overloaded. Batching writes into 100-row groups boosts throughput by orders of magnitude: now they can update up to 500,000 containers per location, with a P95 batch write of 0.1 ms.

They batch using Queues: every 5 seconds each container computes its state and pushes it to a per-location queue, and a Worker consumer processes it with a batch size of 100 and a 1 second batch timeout:

{
    "queues": {
        "consumers": [
            {
                "queue": "production-core-containers-queue-weur",
                "max_batch_size": 100,
                "max_batch_timeout": 1,
                "max_retries": 1
            }
        ]
    }
}

That keeps lag well under 2 seconds. If a queue backs up, each region falls back to a designated backup region until the primary catches up.

Quick Actions got smarter too. Previously a Worker opened a WebSocket to the browser and issued instructions one step at a time: open a page, navigate, wait for load, take the screenshot. Now all the parameters go in a single HTTP request to the container and the whole flow runs internally -- so the DevTools Protocol exchange is faster and response times dropped sharply.

As a bonus, a dedicated container image lets the team upgrade Chrome at their own pace. The much-requested WebGL rendering is now available, along with WebMCP (Model Context Protocol for the web) for new agentic interaction patterns.

Wrap-up

  • Browser Run moved onto Cloudflare Containers: 60 browsers/minute and up to 120 concurrent (4x the old limit)
  • Quick Action response times dropped over 50%, live today with zero work from you
  • State moved from Workers KV to D1 + Queues; 100-row batches allow up to 500,000 containers per location at a 0.1 ms P95 write
  • Regional pools plus backup regions balance latency and reliability
  • Dedicated container images unlock new features like WebGL and WebMCP at the team's own pace

Want to try it? Browser Run is available on all Workers plans. Start with the quick start guide, explore the Quick Actions, or try the /crawl endpoint to deeply extract data by following links across a site. Building AI agents? Check out the Agents SDK with built-in Browser Run support. This one's a treat for any developer who wants to run browsers at scale.