The Infrastructure Behind 14M Cloud Browsers

TinyBrowser is TinyFish's cloud browser, a Chromium browser that runs in its own microVM and that agents and scripts can control over CDP.
Our customers do all sorts of things with it and we use it with our own AI web agent, TinyAgent.
It's at the core of our infrastructure.
We've launched 14 million browsers since May 2025 (5.7 million in October 2026 alone). Now, on a normal day, we launch 220K+ browsers. That's more than 10 times what we launched in March.
It took a while to get there, and a lot is happening under the hood to make our cloud browser infrastructure fast and reliable at that scale.
So we sat down with Pasha Dudka, our Head of Engineering, to understand how TinyBrowser works and what changed since he built the first version.
We'll go over the infrastructure decisions, the learnings, and the mistakes we made along the way (including a $500K fish) as we scaled our cloud browser infrastructure.
A few numbers on TinyBrowser
- 14M browsers launched since May 2025, 5.7M of them in October 2026
- 125 ms at p95 from request to a working CDP URL, with no warm pool of browsers
- One microVM per browser, and one customer per microVM
- 220K to 250K browsers a day, with the busiest day so far at 379K
- More than $700K a year in compute saved by cutting CPU use (about $500K of it from a fish cursor)
We built a cloud browser because our agents needed one
TinyFish started with web agents. Our agents run in the cloud, so the browsers they drive have to run there too. Before building anything, we assessed the cloud browser providers on the market.
"None of them worked for our use case," Pasha says. "Some of them were lacking flexibility; some of them were lacking scalability capabilities. Some of them were overpriced or maybe reasonably priced, but for us, when we need to run thousands and hundreds of thousands of agents in parallel, that's when prices go up very fast, and that's when our economics wouldn't work if we went with an external provider."
So we built our own. "It wasn't the primary product for us," he says.
"You can see that as a by-product, because we build AI agents. AI agents under the hood, they need a browser. So we have to build it."
Pasha owned it for a long time. A dedicated team owns it now, and our agent, TinyAgent, gets its browsers through the same pipeline as every customer who calls TinyBrowser directly.
Every browser gets its own microVM
There were two non-negotiable requirements when building the first version.
The first is isolation. TinyBrowser gives the user direct CDP access, which means full control of the browser. "A user can ask the browser to shut down. A user can start intercepting network traffic, and basically do whatever they want," Pasha says. "That's why there's only one user tenant per browser."
The user is often an agent, and agents can be tricked by what they read. "We still live in the world of prompt injections," he says. Enterprise customers send sensitive data through these browsers, and one customer's session reaching another's would "put the entire company at risk."
The second requirement is speed. We need to start as many browsers as customers ask for, when they ask for them.
Both requirements led to microVMs. Each browser runs in a small virtual machine of its own, and the VM is destroyed when the session ends.
We build them on Unikraft, whose unikernels start fast, keep a small footprint, and can share memory between VMs on the same host.
"Great guys," Pasha says. "I highly recommend them for whatever microVM unikernel use cases anyone has."
How a browser request reaches its microVM
Our browser fleet runs on bare-metal EC2 instances that we call data planes. Each data plane hosts many microVMs, and each microVM is one browser. Pasha's example: if a data plane holds 300 browsers, running 1,000 browsers in parallel takes at least four data planes.

When a request comes in, it passes through a gateway to the control plane, which places the browser on a data plane. The data plane starts the browser from an image stored in a registry and returns a CDP URL. From then on, the client talks directly to the microVM, and our API layer is out of the traffic path.
Startup time performance
At launch in May 2025, a browser took about 2 seconds to start at p95. By January 2026, it was taking 4 to 5 seconds. The cause was features: as we added more of them to the browser, startup slowed down.
The first fix was at the infrastructure layer. Until February 2026, our microVMs ran inside ordinary EC2 virtual machines, so every browser was a VM inside another VM. Moving to bare-metal instances removed the outer layer, and p95 startup dropped to 2 to 3 seconds.
Bare-metal hosts are slower to boot, though. A new one took about 8 minutes to boot at p95, so we had to plan for traffic spikes.
We kept running out of machines
Our browsers can only start on a data plane that is already running, so a traffic spike could use up the available running machines before new ones arrived.
We provisioned for expected traffic plus 50% headroom, and it wasn't enough. "Every week we had like five, six incidents when, well, you don't have resources anymore, and now we start throwing errors at the user," Pasha says.
We fixed it in two steps.
The first step was to build our own AMIs
An AMI (Amazon Machine Image) is the template AWS uses to launch an EC2 instance. We built our own with everything pre-installed, so nothing installs at boot. Doing this reduced the boot time of a bare-metal instance from about 8 minutes to about 4 minutes.
The second was to add a warm pool of stopped EC2 instances
Now we have a pool of about 30 stopped EC2 instances. It allows us to start serving browsers in 20 to 30 seconds and absorb traffic spikes much faster. With a stopped instance, we mostly pay for its EBS volume, so it's a very good trade-off for us.
Pasha says he can't remember the last time we ran out of capacity. Beyond the spikes we plan for, now adding more capacity is a spending decision. "Now it's basically the function of money versus reliability," he says.
The next change is in the API layer: "Instead of throwing the error to the user immediately, we can introduce queues and make a user wait a little bit rather than failing immediately."
We haven't built it yet because we haven't run out of capacity in a long time, but we'll likely add it as a backup as we grow.
From 2 to 3 seconds to 125 ms for a fresh browser
Optimizing for traffic spikes like this didn't improve the browser startup time. It still took 2 to 3 seconds each to start.
The usual answer in this scenario is a warm pool: start browsers ahead of time, keep them running, and hand one out when a request arrives.
At our scale, that would mean keeping hundreds, or even thousands, of browsers in a warm pool, wasting resources. So we kept the warm pool option as a backup plan and explored how we could reduce the startup time instead.
That's what led us to RAM-snapshot templates in June 2026, a Unikraft feature. "The concept is very simple," Pasha says. "You front-load all the expensive work."
We build the template once:
- Start a browser and the processes around it. This is the slow part.
- Pause the VM once everything is running. In Pasha's words, "you virtually click a pause on that and take a snapshot of RAM and store it on disk."
- Keep that snapshot as the template.
Every request then gets a clone of the template. The clone wakes up as a browser that has already started, and loading it into memory takes 2 to 3 milliseconds. A short setup step then applies what differs between customers, such as proxy settings and other browser configuration.
We measure the whole round trip, from the moment a user asks for a browser to the moment they have a working CDP URL, including network hops and load balancers.
That round trip is now 125 ms at p95.

"Since it's that fast, we don't need a warm pool of browsers," Pasha says. A pool might save a few milliseconds, which he calls "completely negligible."
So we run a warm pool of machines, but there is no warm pool for browsers. With that setup, we can handle spikes and serve browsers very fast.
Those changes had the biggest impact on user experience. What's left is mostly efficiency work that lowers our costs.
Optimizing for memory
With startup and capacity under control, the limit became how many browsers fit on one data plane.
"Chromium is extremely hungry for resources, CPU, RAM," Pasha says. Chromium with a single page open uses about 700 MB of RAM before it does any work.
To reduce RAM use, we used another feature of Unikraft that lets VMs on the same host share part of their memory.
It works like this: part of those 700 MB is the same in every browser, because it holds the static parts of Chromium. Hundreds of browsers on the same host can read that part from a single copy.
We measured 300 to 400 MB per browser that can be shared. That change more than halved the RAM each browser needs.

"At that point, we started being CPU-bound, not RAM-bound," Pasha says. CPU decided how many browsers fit on a data plane for a long time after that.
Optimizing for CPU and the $500K fish
For a long time, we didn't have browser-level telemetry, so we couldn't see CPU use per browser. Once we implemented per-browser telemetry, the numbers looked wrong. "Something felt off," Pasha says. "Sometimes it felt like really, really abusive on CPU."

The biggest cause was a fish. Pasha calls it "clowny" and describes it like this:
"At some point we decided that it would be a nice touch to replace default mouse cursor with a cute animated fish. This animated fish would animate at 20fps triggering a full page re-render. Given that cloud browsers use CPU for rendering, such animation ended up being extremely expensive for CPU and would drive CPU consumption through the roof."
Our browsers use the CPU to draw pages. With the fish animating 20 times a second, every browser on the fleet was redrawing its whole page 20 times a second for the whole session.
Nobody noticed for months.
"When we introduced this animated fish, per-browser telemetry didn't exist, so such regression went unnoticed and was sitting there for many months."
Total CPU use rises with traffic, and because we were growing fast, the extra CPU cost hid inside that growth. "Simply disabling animation for that icon dramatically reduced our CPU footprint and thus the amount of AWS resources needed."

Turning it off saves us about $500K a year.
The same telemetry pointed at the rest of the CPU work. Some of it was in how our agent drives the browser, and some was in Chromium's startup flags. "There are a gazillion different startup flags that you can play with," Pasha says. Together with the fish, those changes cut p95 CPU per browser by more than 40% and saved more than $700K a year in compute.
That made memory the limit again. Our data planes use instance types chosen for CPU, and a type with more memory would cost about 5% more per instance and fit about 40% more browsers. "That thing is still not done," Pasha says. "It's still in my to-do list somewhere."
Daily volume grew 10x with the same startup time
Daily browser launches went from 22K in March 2026 to 34K in July and 112K by mid-September. Today we run 220K to 250K a day, and the busiest day so far reached about 379K browser launches.

September was also when TinyFish went from 100K to 200K users in 18 days. The browser layer wasn't where the trouble was. "Luckily, that was the least of our concerns," Pasha says. "Other parts of our ecosystem, other parts of our infrastructure started melting way faster."
The capacity work had started months earlier, after a couple of smaller spikes made it clear the browser layer needed it.
Our agents also got faster, which freed browsers sooner. "If it takes less time for an agent to finish its work, it holds a browser for less time," Pasha says, so the same infrastructure can serve more browser runs.
On a quiet day, about 1,500 browsers are running at any moment. A big spike can quadruple that easily.
The incidents we caused ourselves
Not every incident came from traffic spikes. One long-running source of issues came from our own release pipeline.
Our browser images live in a registry, and the control plane starts the browsers from them. Early on, the release process for a new image was, in Pasha's words, "pretty hand-wavy."
He describes what went wrong:
"Someone changing the browser image sends a PR. And through multiple events that coincide, that image ends up in the repository or doesn't end up in the repository, and the control plane assumes it's there, but it's not there. And now all of the sudden, the entire traffic just drops."
"Some may argue that a serious company should not end up in this situation," he says. "But we also evolve. We also start from small. We take shortcuts."
Shipping fast means some of those shortcuts will fail later, and the team plans for it. "It's not the question of if it's gonna backfire," Pasha says. "It's only a matter of when and how bad it's gonna be."
Each incident ends with action items.
In Pasha's words, the fixes "probably didn't completely prevent it, but at least reduced the likelihood of something like this happening again."
What we're optimizing next
The next limit is the chain of autoscalers in front of the browsers.
TinyAgent requests browsers the same way an external customer does, so a spike has to travel through every layer before the browser fleet sees it.
Pasha puts it this way:
"A traffic spike starts at the API gateway, right? So first one to react is the API gateway. Then as more jobs start getting funneled into our job queues, workers start scaling up. And only then when those workers start working, then they start requesting more browsers, and only then our browser infrastructure realizes, 'Oh, we have a spike. I gotta scale up.'"
AWS waits several ticks at each layer before it treats a rise as real traffic.
So the plan is to run the agent and its browser together. Today they talk over the network, even inside the same VPC.
Putting them on the same machine would cut that latency, remove the separate scaling layer for browsers and let us plan CPU and RAM for agent and browser as one unit.
It also lets us act earlier. Our bulk API takes many runs in one request, so the size of the job is known up front. "Right at the moment when a user asks us to do 1,000 agent runs, right at that point we know that we need to spin up enough resources for 1,000 runs," Pasha says.
The design is still on the whiteboard, and it might not survive testing: "We think it's gonna work, if it doesn't work, then let's go back to where we are. That can also happen."
Demand forecasting is further out. "All our ML and AI resources are fully utilized for making our agent better, so we literally don't have spare ML brainpower," Pasha says.
Run your agents on TinyBrowser
TinyBrowser is the infrastructure described in this post. Our own agent, TinyAgent, gets its browsers from the same fleet and the same pipeline as every customer, so your agents get:
- Speed at scale. A fresh browser is ready in 125 ms at p95, from request to a working CDP URL, on days when we launch more than 220K browsers. Through September's traffic surge, median startup time held at 75 ms the entire time.
- Room for spikes. We plan capacity for spikes of four times our normal load and keep machines in reserve, so your agents can fan out to hundreds of browsers without you provisioning anything.
- Reliable sessions. Every browser runs in its own microVM, starts from a clean state and is destroyed when the session ends. Fingerprinting, proxy routing and anti-bot handling run at the infrastructure layer.
- Your code as it is. Connect Playwright, Puppeteer or any CDP client. We run the machines, the scaling and the sessions.
You can start with TinySearch and TinyFetch, which are free within limits. When your agents need browsers, add funds to your prepaid Wallet. TinyBrowser usage is billed at $0.002 per browser minute from the Wallet (see our pricing page for current rates).
If you want the website work handled too, TinyAgent runs on the same browsers and operates the pages for you, with TinyVault and TinyProfiles handling sign-ins for authorized workflows.
Create your TinyFish account to get your API key.
AI disclosure
Content on this website may be created or refined with the assistance of AI tools and is subject to human editorial review.



