From 1309ab9da4cf673d6ffd380e423f3cdbae2a6283 Mon Sep 17 00:00:00 2001 From: dprevoznik <58714078+dprevoznik@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:03:00 +0000 Subject: [PATCH 01/14] Clarify when to use browser pools --- introduction/create.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/introduction/create.mdx b/introduction/create.mdx index 4ee4d5af..fd7dcf60 100644 --- a/introduction/create.mdx +++ b/introduction/create.mdx @@ -89,9 +89,9 @@ Most of what you'll tune at creation time falls into four buckets: ## On demand or from a pool -`browsers.create()` boots a browser for you on the spot. That's the right call while you're building, and for workloads that run occasionally. +`browsers.create()` boots a browser for you on the spot. That's the right call while you're building, and for occasional, bursty, or one-off workloads. -Once you're running the same task repeatedly — or more than a handful at a time — create a [browser pool](/browsers/pools) instead. A browser pool holds browsers that are already booted with your configuration applied, so `acquire` hands you one that's ready to drive rather than starting one from scratch. Two things get faster: configurations that restart Chromium on creation (custom viewports, extensions, kiosk mode) are already applied, and acquiring from a browser pool sidesteps the [rate limit](/info/pricing#rate-limiting) on browser creation that you'll otherwise hit at volume. +Use a [browser pool](/browsers/pools) when every session in a workload needs the same exact configuration and you need to run that workload at scale. Pools are also the right choice when you need to acquire browsers at a rate that would exceed the [rate limit](/info/pricing#rate-limiting) on `browsers.create()`. A browser pool holds browsers that are already booted with your configuration applied, so `acquire` hands you one that's ready to drive rather than starting one from scratch. Configurations that restart Chromium on creation, such as custom viewports, extensions, and kiosk mode, are already applied. ```typescript Typescript/Javascript From 4ae4855aa7ebbe6e6c37917bc6fd07d648e59c0c Mon Sep 17 00:00:00 2001 From: dprevoznik <58714078+dprevoznik@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:25:36 +0000 Subject: [PATCH 02/14] Clarify create and pool scaling guidance --- introduction/create.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/introduction/create.mdx b/introduction/create.mdx index fd7dcf60..d7eb458b 100644 --- a/introduction/create.mdx +++ b/introduction/create.mdx @@ -89,9 +89,9 @@ Most of what you'll tune at creation time falls into four buckets: ## On demand or from a pool -`browsers.create()` boots a browser for you on the spot. That's the right call while you're building, and for occasional, bursty, or one-off workloads. +`browsers.create()` boots a browser for you on the spot. That's the right call while you're building, for occasional, bursty, or one-off workloads, and when your configuration changes per user as you scale. -Use a [browser pool](/browsers/pools) when every session in a workload needs the same exact configuration and you need to run that workload at scale. Pools are also the right choice when you need to acquire browsers at a rate that would exceed the [rate limit](/info/pricing#rate-limiting) on `browsers.create()`. A browser pool holds browsers that are already booted with your configuration applied, so `acquire` hands you one that's ready to drive rather than starting one from scratch. Configurations that restart Chromium on creation, such as custom viewports, extensions, and kiosk mode, are already applied. +Start considering a [browser pool](/browsers/pools) once you're scaling beyond self-serve tiers and every run in a workload uses the same attributes. Pools are also the right choice when you need to acquire browsers at a rate that would exceed the [rate limit](/info/pricing#rate-limiting) on `browsers.create()`. A browser pool holds browsers that are already booted with your configuration applied, so `acquire` hands you one that's ready to drive rather than starting one from scratch. Configurations that restart Chromium on creation, such as custom viewports, extensions, and kiosk mode, are already applied. ```typescript Typescript/Javascript From 761ec907a3bb66981be32cca55374a9907a7eb3c Mon Sep 17 00:00:00 2001 From: dprevoznik <58714078+dprevoznik@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:29:02 +0000 Subject: [PATCH 03/14] Remove extra create workload guidance --- introduction/create.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/introduction/create.mdx b/introduction/create.mdx index d7eb458b..0ea3e0ab 100644 --- a/introduction/create.mdx +++ b/introduction/create.mdx @@ -89,7 +89,7 @@ Most of what you'll tune at creation time falls into four buckets: ## On demand or from a pool -`browsers.create()` boots a browser for you on the spot. That's the right call while you're building, for occasional, bursty, or one-off workloads, and when your configuration changes per user as you scale. +`browsers.create()` boots a browser for you on the spot. That's the right call while you're building and when your configuration changes per user as you scale. Start considering a [browser pool](/browsers/pools) once you're scaling beyond self-serve tiers and every run in a workload uses the same attributes. Pools are also the right choice when you need to acquire browsers at a rate that would exceed the [rate limit](/info/pricing#rate-limiting) on `browsers.create()`. A browser pool holds browsers that are already booted with your configuration applied, so `acquire` hands you one that's ready to drive rather than starting one from scratch. Configurations that restart Chromium on creation, such as custom viewports, extensions, and kiosk mode, are already applied. From 8de078594d03f30d3caf846c77c598a5e61b9683 Mon Sep 17 00:00:00 2001 From: dprevoznik <58714078+dprevoznik@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:29:51 +0000 Subject: [PATCH 04/14] Add enterprise rate limit guidance --- introduction/create.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/introduction/create.mdx b/introduction/create.mdx index 0ea3e0ab..bef61997 100644 --- a/introduction/create.mdx +++ b/introduction/create.mdx @@ -91,7 +91,7 @@ Most of what you'll tune at creation time falls into four buckets: `browsers.create()` boots a browser for you on the spot. That's the right call while you're building and when your configuration changes per user as you scale. -Start considering a [browser pool](/browsers/pools) once you're scaling beyond self-serve tiers and every run in a workload uses the same attributes. Pools are also the right choice when you need to acquire browsers at a rate that would exceed the [rate limit](/info/pricing#rate-limiting) on `browsers.create()`. A browser pool holds browsers that are already booted with your configuration applied, so `acquire` hands you one that's ready to drive rather than starting one from scratch. Configurations that restart Chromium on creation, such as custom viewports, extensions, and kiosk mode, are already applied. +Start considering a [browser pool](/browsers/pools) once you're scaling beyond self-serve tiers and every run in a workload uses the same attributes. Pools are also the right choice when you need to acquire browsers at a rate that would exceed the [rate limit](/info/pricing#rate-limiting) on `browsers.create()`. If you're on the Enterprise tier and rate limiting is a concern, speak with your account manager to assess the best path forward. A browser pool holds browsers that are already booted with your configuration applied, so `acquire` hands you one that's ready to drive rather than starting one from scratch. Configurations that restart Chromium on creation, such as custom viewports, extensions, and kiosk mode, are already applied. ```typescript Typescript/Javascript From 4459ed173b26f7ceac26ce8d6aaec37e43a3365b Mon Sep 17 00:00:00 2001 From: dprevoznik <58714078+dprevoznik@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:32:02 +0000 Subject: [PATCH 05/14] Clarify enterprise rate limit guidance --- introduction/create.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/introduction/create.mdx b/introduction/create.mdx index bef61997..9fd6413d 100644 --- a/introduction/create.mdx +++ b/introduction/create.mdx @@ -91,7 +91,7 @@ Most of what you'll tune at creation time falls into four buckets: `browsers.create()` boots a browser for you on the spot. That's the right call while you're building and when your configuration changes per user as you scale. -Start considering a [browser pool](/browsers/pools) once you're scaling beyond self-serve tiers and every run in a workload uses the same attributes. Pools are also the right choice when you need to acquire browsers at a rate that would exceed the [rate limit](/info/pricing#rate-limiting) on `browsers.create()`. If you're on the Enterprise tier and rate limiting is a concern, speak with your account manager to assess the best path forward. A browser pool holds browsers that are already booted with your configuration applied, so `acquire` hands you one that's ready to drive rather than starting one from scratch. Configurations that restart Chromium on creation, such as custom viewports, extensions, and kiosk mode, are already applied. +Start considering a [browser pool](/browsers/pools) once you're scaling beyond self-serve tiers and every run in a workload uses the same attributes. Pools are also the right choice when you need to acquire browsers at a rate that would exceed the [rate limit](/info/pricing#rate-limiting) on `browsers.create()`. If you're on the Enterprise tier, speak with your account manager about applicable rate limits and what's best for your workloads. A browser pool holds browsers that are already booted with your configuration applied, so `acquire` hands you one that's ready to drive rather than starting one from scratch. Configurations that restart Chromium on creation, such as custom viewports, extensions, and kiosk mode, are already applied. ```typescript Typescript/Javascript From 34a5a89af035954190e29d37f8452a8bb8e6a72d Mon Sep 17 00:00:00 2001 From: dprevoznik <58714078+dprevoznik@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:36:21 +0000 Subject: [PATCH 06/14] Clarify browser pool recommendation --- introduction/create.mdx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/introduction/create.mdx b/introduction/create.mdx index 9fd6413d..a7b4e7a0 100644 --- a/introduction/create.mdx +++ b/introduction/create.mdx @@ -91,7 +91,13 @@ Most of what you'll tune at creation time falls into four buckets: `browsers.create()` boots a browser for you on the spot. That's the right call while you're building and when your configuration changes per user as you scale. -Start considering a [browser pool](/browsers/pools) once you're scaling beyond self-serve tiers and every run in a workload uses the same attributes. Pools are also the right choice when you need to acquire browsers at a rate that would exceed the [rate limit](/info/pricing#rate-limiting) on `browsers.create()`. If you're on the Enterprise tier, speak with your account manager about applicable rate limits and what's best for your workloads. A browser pool holds browsers that are already booted with your configuration applied, so `acquire` hands you one that's ready to drive rather than starting one from scratch. Configurations that restart Chromium on creation, such as custom viewports, extensions, and kiosk mode, are already applied. +Consider a [browser pool](/browsers/pools) once you've scaled beyond self-serve tiers and every run in a workload uses the same attributes. Pools are also a solid choice when you need to acquire browsers at a rate that would exceed the [rate limit](/info/pricing#rate-limiting) on `browsers.create()`. + + + If you're on an Enterprise plan, speak with your account manager about applicable rate limits for `browsers.create()` and what's best for your workloads. + + +A browser pool holds browsers that are already booted with your configuration applied, so `acquire` hands you one that's ready to drive rather than starting one from scratch. Configurations that restart Chromium on creation, such as custom viewports, extensions, and kiosk mode, are already applied. ```typescript Typescript/Javascript From 85ad1dff49f9fc40bacba8822c30814e9694d8e1 Mon Sep 17 00:00:00 2001 From: dprevoznik <58714078+dprevoznik@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:43:59 +0000 Subject: [PATCH 07/14] Refine browser pool scaling guidance --- introduction/create.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/introduction/create.mdx b/introduction/create.mdx index a7b4e7a0..61f232b6 100644 --- a/introduction/create.mdx +++ b/introduction/create.mdx @@ -91,7 +91,7 @@ Most of what you'll tune at creation time falls into four buckets: `browsers.create()` boots a browser for you on the spot. That's the right call while you're building and when your configuration changes per user as you scale. -Consider a [browser pool](/browsers/pools) once you've scaled beyond self-serve tiers and every run in a workload uses the same attributes. Pools are also a solid choice when you need to acquire browsers at a rate that would exceed the [rate limit](/info/pricing#rate-limiting) on `browsers.create()`. +Consider a [browser pool](/browsers/pools) once you've built and scaled your workload and every run in a workload uses the same attributes. Pools are also a solid choice when you need to acquire browsers at a rate that would exceed the [rate limit](/info/pricing#rate-limiting) on `browsers.create()`. If you're on an Enterprise plan, speak with your account manager about applicable rate limits for `browsers.create()` and what's best for your workloads. From 92f70fb829f6039647782be36f4ff936f4812d4b Mon Sep 17 00:00:00 2001 From: dprevoznik <58714078+dprevoznik@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:47:28 +0000 Subject: [PATCH 08/14] Align scale guidance with browser creation --- introduction/scale.mdx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/introduction/scale.mdx b/introduction/scale.mdx index 53947ebb..30ca86d8 100644 --- a/introduction/scale.mdx +++ b/introduction/scale.mdx @@ -10,25 +10,29 @@ This guide covers how to run Kernel in production at scale — which architectur A [browser pool](/browsers/pools) keeps a set of identically-configured browsers ready for immediate use. Compared to creating browsers on demand, it gives you: -- **Low-latency acquisition** — the browser is already booted with your configuration applied (including settings like custom viewports, extensions, and kiosk-mode live view that otherwise [restart Chromium](/browsers/performance#troubleshooting-latency) on a fresh browser), so `acquire` hands you one that's ready to drive. +- **Lowest-latency acquisition** — the browser is already booted with your configuration applied (including settings like custom viewports, extensions, and kiosk-mode live view that otherwise [restart Chromium](/browsers/performance#troubleshooting-latency) on a fresh browser), so `acquire` hands you one that's ready to drive. - **Reserved, pre-configured capacity** — a fixed set of browsers on your exact configuration, ready before traffic arrives. - **Higher creation throughput** — acquiring from a pool isn't subject to the [rate limit](/info/pricing#rate-limiting) on `browsers.create()` that high-volume workloads hit. + + If you're on an Enterprise plan, speak with your account manager about applicable rate limits for `browsers.create()` and what's best for your workloads. + + The tradeoff: a browser pool counts against your concurrency limit whether or not its browsers are currently acquired — a pool sized to 40 holds 40 of your limit. Idle pooled browsers aren't billed, but they hold the slot. ## When to use a pool vs on-demand Reach for a **browser pool** when: -- you're running the same workload repeatedly, in production +- you've built and scaled your workload, and every run uses the same workload attributes - acquisition latency matters — a cold start is unacceptable (for example, a synchronous, user-facing action) - traffic is steady or high-frequency enough to keep the browser pool utilized - you're hitting the `browsers.create()` rate limit at volume Stick with **on-demand `browsers.create()`** when: -- volume is low, bursty, one-off, or you're still developing -- each session needs a different configuration (a pool is one fixed config) +- you're still building +- your configuration changes per user (a pool is one fixed config) - you need a GPU browser (not available in pools) Concurrency and request patterns are how you *size* a pool once you've decided to use one — not a threshold that gates whether pools are worth it. Even a small pool pays off when acquisition latency matters and demand is steady. From 8231b3848bbd4d74e7c2963d26115474d66fd96a Mon Sep 17 00:00:00 2001 From: dprevoznik <58714078+dprevoznik@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:48:06 +0000 Subject: [PATCH 09/14] Lead scale guidance with on-demand browsers --- introduction/scale.mdx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/introduction/scale.mdx b/introduction/scale.mdx index 30ca86d8..65bb335f 100644 --- a/introduction/scale.mdx +++ b/introduction/scale.mdx @@ -22,6 +22,12 @@ The tradeoff: a browser pool counts against your concurrency limit whether or no ## When to use a pool vs on-demand +Stick with **on-demand `browsers.create()`** when: + +- you're still building +- your configuration changes per user (a pool is one fixed config) +- you need a GPU browser (not available in pools) + Reach for a **browser pool** when: - you've built and scaled your workload, and every run uses the same workload attributes @@ -29,12 +35,6 @@ Reach for a **browser pool** when: - traffic is steady or high-frequency enough to keep the browser pool utilized - you're hitting the `browsers.create()` rate limit at volume -Stick with **on-demand `browsers.create()`** when: - -- you're still building -- your configuration changes per user (a pool is one fixed config) -- you need a GPU browser (not available in pools) - Concurrency and request patterns are how you *size* a pool once you've decided to use one — not a threshold that gates whether pools are worth it. Even a small pool pays off when acquisition latency matters and demand is steady. ## Sizing From 3c39ef14005f2f009cf195feba6360c4493d9434 Mon Sep 17 00:00:00 2001 From: dprevoznik <58714078+dprevoznik@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:48:27 +0000 Subject: [PATCH 10/14] Clarify scale decision heading --- introduction/scale.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/introduction/scale.mdx b/introduction/scale.mdx index 65bb335f..26d59dce 100644 --- a/introduction/scale.mdx +++ b/introduction/scale.mdx @@ -20,7 +20,7 @@ A [browser pool](/browsers/pools) keeps a set of identically-configured browsers The tradeoff: a browser pool counts against your concurrency limit whether or not its browsers are currently acquired — a pool sized to 40 holds 40 of your limit. Idle pooled browsers aren't billed, but they hold the slot. -## When to use a pool vs on-demand +## Is on-demand or browser pools best for my workload? Stick with **on-demand `browsers.create()`** when: From db727859edea3becc9f456e5fef6813842de6e39 Mon Sep 17 00:00:00 2001 From: dprevoznik <58714078+dprevoznik@users.noreply.github.com> Date: Mon, 28 Sep 2026 14:14:11 +0000 Subject: [PATCH 11/14] Streamline create and scale guidance on browser pools Co-Authored-By: Claude Opus 5.5 --- browsers/pools.mdx | 1 - info/pricing.mdx | 2 +- introduction/create.mdx | 50 +++---------------------------------- introduction/scale.mdx | 55 ++++++++++++++++++++--------------------- 4 files changed, 31 insertions(+), 77 deletions(-) diff --git a/browsers/pools.mdx b/browsers/pools.mdx index d83958ee..26b06cb3 100644 --- a/browsers/pools.mdx +++ b/browsers/pools.mdx @@ -31,7 +31,6 @@ A few constraints to weigh before moving a workload onto a browser pool: - **One fixed configuration per browser pool**, with `start_url` the only setting you can override per acquisition — see [Create a browser pool](#create-a-browser-pool). - **A profile set on the browser pool loads read-only**, and a browser pool holds one at a time — see [Per-user profiles with browser pools](#per-user-profiles-with-browser-pools) for how to persist state per user. - **Browser pool capacity counts against your [concurrency limit](/info/pricing#concurrency-limits)** whether or not its browsers are acquired, though idle pooled browsers aren't billed. -- **Plan-gated.** Browser pools are available on the Start-Up and Enterprise plans. ## Create a browser pool diff --git a/info/pricing.mdx b/info/pricing.mdx index 69b7e5c1..674bd447 100644 --- a/info/pricing.mdx +++ b/info/pricing.mdx @@ -108,7 +108,7 @@ The [app invocation limits](#concurrency-limits) are concurrency limits, not a n you pay the standard usage-based price per GB-second while browsers are running. Idle browsers in a pool incur no disk charges—you only pay when a browser is actively in use. - Note: A browser pool counts toward your concurrency limit whether or not its browsers are currently acquired — a browser pool sized to 40 browsers uses 40 of your limit. Browser pools are available on Start-Up and Enterprise plans. + Note: A browser pool counts toward your concurrency limit whether or not its browsers are currently acquired — a browser pool sized to 40 browsers uses 40 of your limit. Managed Auth is included on all plans with no per-connection fees. It uses browser sessions for login, health checks, and eligible automatic reauthentication. These count toward your browser usage and concurrency like any other browser session. diff --git a/introduction/create.mdx b/introduction/create.mdx index 61f232b6..cc0e26a5 100644 --- a/introduction/create.mdx +++ b/introduction/create.mdx @@ -89,58 +89,14 @@ Most of what you'll tune at creation time falls into four buckets: ## On demand or from a pool -`browsers.create()` boots a browser for you on the spot. That's the right call while you're building and when your configuration changes per user as you scale. +`browsers.create()` boots a browser for you on the spot. It's the default while you're building, and it stays the right fit when your configuration changes per user as you scale. -Consider a [browser pool](/browsers/pools) once you've built and scaled your workload and every run in a workload uses the same attributes. Pools are also a solid choice when you need to acquire browsers at a rate that would exceed the [rate limit](/info/pricing#rate-limiting) on `browsers.create()`. +A [browser pool](/browsers/pools) keeps browsers already booted on one fixed configuration, so `acquire` hands you one that's ready to drive. Consider one once you've built and scaled your workload and every run uses the same attributes, or when you need to acquire browsers faster than the [rate limit](/info/pricing#rate-limiting) on `browsers.create()` allows. See [Scale](/introduction/scale) to decide which fits your workload. If you're on an Enterprise plan, speak with your account manager about applicable rate limits for `browsers.create()` and what's best for your workloads. -A browser pool holds browsers that are already booted with your configuration applied, so `acquire` hands you one that's ready to drive rather than starting one from scratch. Configurations that restart Chromium on creation, such as custom viewports, extensions, and kiosk mode, are already applied. - - -```typescript Typescript/Javascript -// Create the pool once, at deploy time or on startup -await kernel.browserPools.create({ name: 'checkout-pool', size: 20, stealth: true }); - -// Then, wherever your automation runs -const kernelBrowser = await kernel.browserPools.acquire('checkout-pool', {}); -``` - -```python Python -# Create the pool once, at deploy time or on startup -kernel.browser_pools.create(name="checkout-pool", size=20, stealth=True) - -# Then, wherever your automation runs -kernel_browser = kernel.browser_pools.acquire("checkout-pool") -``` - -```go Go -// Create the pool once, at deploy time or on startup -if _, err := client.BrowserPools.New(ctx, kernel.BrowserPoolNewParams{ - Name: kernel.String("checkout-pool"), - Size: 20, - Stealth: kernel.Bool(true), -}); err != nil { - panic(err) -} - -// Then, wherever your automation runs -kernelBrowser, err := client.BrowserPools.Acquire(ctx, "checkout-pool", kernel.BrowserPoolAcquireParams{}) -if err != nil { - panic(err) -} -``` - -```bash CLI -kernel browser-pools create checkout-pool --size 20 --stealth -kernel browser-pools acquire checkout-pool -``` - - -An acquired browser returns the same fields as one you created directly, so the rest of your code is identical. Idle browsers in a browser pool aren't billed — you pay only while a browser is acquired and running — though browser pool capacity does count against your [concurrency limit](/info/pricing#concurrency-limits). - ## Lifecycle A browser stays alive as long as something is driving it — a CDP or WebDriver client, a [Live View](/browsers/live-view) viewer, or an in-flight [computer controls](/browsers/computer-controls) request. After five seconds with none of those active, it enters [standby](/browsers/standby) — state is preserved, billing stops. Once in standby, after the configurable timeout (60s by default) elapses it's deleted. @@ -270,4 +226,4 @@ Once you have a browser, you need to drive it. Head to [Control](/introduction/c To carry cookies, site data, tabs, and preferences into later runs, use [Browser Profiles](/browsers/profiles). Profiles can resume an agent workflow, maintain one identity per user or account, or give parallel workers the same read-only starting state. -When you're ready to run this in production, the [Browser Pools overview](/browsers/pools) shows how to serve your workload from a pool of pre-provisioned browsers instead of creating one per task. +When you're ready to run this in production, [Scale](/introduction/scale) covers whether to keep creating browsers on demand or serve your workload from a [browser pool](/browsers/pools), and the architecture patterns for each. diff --git a/introduction/scale.mdx b/introduction/scale.mdx index 26d59dce..1ded27fd 100644 --- a/introduction/scale.mdx +++ b/introduction/scale.mdx @@ -4,23 +4,9 @@ description: "Recommended practices for scaling in production" --- ## Overview -This guide covers how to run Kernel in production at scale — which architecture to build around browser creation, and when to reach for a browser pool. It assumes you're comfortable [creating](/introduction/create) and [controlling](/introduction/control) browsers; for the mechanics of standing up a pool and acquiring from it, see [Browser Pools](/browsers/pools). +This guide covers how to run Kernel in production at scale — whether to create browsers on demand or serve them from a browser pool, and which architecture to build around that choice. It assumes you're comfortable [creating](/introduction/create) and [controlling](/introduction/control) browsers; for the mechanics of standing up a pool and acquiring from it, see [Browser Pools](/browsers/pools). -## Why a browser pool - -A [browser pool](/browsers/pools) keeps a set of identically-configured browsers ready for immediate use. Compared to creating browsers on demand, it gives you: - -- **Lowest-latency acquisition** — the browser is already booted with your configuration applied (including settings like custom viewports, extensions, and kiosk-mode live view that otherwise [restart Chromium](/browsers/performance#troubleshooting-latency) on a fresh browser), so `acquire` hands you one that's ready to drive. -- **Reserved, pre-configured capacity** — a fixed set of browsers on your exact configuration, ready before traffic arrives. -- **Higher creation throughput** — acquiring from a pool isn't subject to the [rate limit](/info/pricing#rate-limiting) on `browsers.create()` that high-volume workloads hit. - - - If you're on an Enterprise plan, speak with your account manager about applicable rate limits for `browsers.create()` and what's best for your workloads. - - -The tradeoff: a browser pool counts against your concurrency limit whether or not its browsers are currently acquired — a pool sized to 40 holds 40 of your limit. Idle pooled browsers aren't billed, but they hold the slot. - -## Is on-demand or browser pools best for my workload? +## Should I use on-demand browsers or a browser pool? Stick with **on-demand `browsers.create()`** when: @@ -31,11 +17,23 @@ Stick with **on-demand `browsers.create()`** when: Reach for a **browser pool** when: - you've built and scaled your workload, and every run uses the same workload attributes -- acquisition latency matters — a cold start is unacceptable (for example, a synchronous, user-facing action) +- you need the lowest possible acquisition latency (for example, a synchronous, user-facing action) - traffic is steady or high-frequency enough to keep the browser pool utilized - you're hitting the `browsers.create()` rate limit at volume -Concurrency and request patterns are how you *size* a pool once you've decided to use one — not a threshold that gates whether pools are worth it. Even a small pool pays off when acquisition latency matters and demand is steady. + + If you're on an Enterprise plan, speak with your account manager about applicable rate limits for `browsers.create()` and what's best for your workloads. + + +## What a browser pool gives you + +A [browser pool](/browsers/pools) keeps a set of identically-configured browsers ready for immediate use. Compared to creating browsers on demand, it gives you: + +- **Lowest-latency acquisition** — the browser is already booted with your configuration applied (including settings like custom viewports, extensions, and kiosk-mode live view that otherwise [restart Chromium](/browsers/performance#troubleshooting-latency) on a fresh browser), so `acquire` hands you one that's ready to drive. +- **Reserved, pre-configured capacity** — a fixed set of browsers on your exact configuration, ready before traffic arrives. +- **Higher creation throughput** — acquiring from a pool isn't subject to the [rate limit](/info/pricing#rate-limiting) on `browsers.create()` that high-volume workloads hit. + +The tradeoff: a browser pool counts against your concurrency limit whether or not its browsers are currently acquired — a pool sized to 40 holds 40 of your limit. Idle pooled browsers aren't billed, but they hold the slot. ## Sizing @@ -43,14 +41,14 @@ Watch `available_count` and target 10–20% available under normal load, resizin ## Architecture patterns -### Direct browser creation (POC) +### On-demand creation -For proof-of-concept work and early production systems with modest concurrency needs, creating browsers on-demand is the simplest approach. +Creating a browser per task is the simplest approach. It's the right fit while you're building and when each task needs its own configuration. **When to use:** -- Low or unpredictable volume -- Infrequent or one-off workloads - Early development and testing +- Configuration that changes per user or per task +- GPU browsers @@ -88,9 +86,9 @@ async function processTask(taskData: any) { ``` -### Single browser pool (scaling) +### Single browser pool -For production systems with consistent, high-frequency workloads, a browser pool allows you to access higher concurrency plus predictable performance. +For production workloads that run on the same configuration every time, a browser pool hands you ready-to-drive browsers, and acquiring from it isn't subject to the `browsers.create()` rate limit. **When to use:** - Consistent, high-frequency workloads on a fixed configuration @@ -155,12 +153,12 @@ async function processTask(taskData: any) { - Always release browsers in a `finally` block to prevent browser pool exhaustion - Set `acquire_timeout_seconds` based on your SLA requirements -### Queue-based processing (high scale) +### Queue-based processing -For systems exceeding browser pool capacity or with unpredictable bursts, implement a task queue to manage workloads gracefully. +When request volume exceeds your concurrency or traffic arrives in unpredictable bursts, put a task queue in front of your browsers. The example below acquires from a browser pool; the same pattern works on demand, with `browsers.create()` in place of `acquire` and `deleteByID` in place of `release`. **When to use:** -- Request volume exceeds a single browser pool's capacity +- Request volume exceeds your available concurrency - Highly variable traffic patterns - Need to prioritize certain tasks - Want to decouple request ingestion from processing @@ -168,8 +166,9 @@ For systems exceeding browser pool capacity or with unpredictable bursts, implem ```typescript -import { Queue } from 'bullmq'; // or any queue system +import { Queue, Worker } from 'bullmq'; // or any queue system import Kernel from '@onkernel/sdk'; +import { chromium } from 'playwright'; const kernel = new Kernel(); const POOL_NAME = 'production-pool'; From 07aab18d6de04372f6e90e3ca89002a13ca9b754 Mon Sep 17 00:00:00 2001 From: dprevoznik <58714078+dprevoznik@users.noreply.github.com> Date: Mon, 28 Sep 2026 19:42:50 +0000 Subject: [PATCH 12/14] Reword pool latency example Co-Authored-By: Claude Opus 5.5 --- introduction/scale.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/introduction/scale.mdx b/introduction/scale.mdx index 1ded27fd..98739bb7 100644 --- a/introduction/scale.mdx +++ b/introduction/scale.mdx @@ -17,7 +17,7 @@ Stick with **on-demand `browsers.create()`** when: Reach for a **browser pool** when: - you've built and scaled your workload, and every run uses the same workload attributes -- you need the lowest possible acquisition latency (for example, a synchronous, user-facing action) +- you need the lowest possible acquisition latency (for example, a heavily customized browser config) - traffic is steady or high-frequency enough to keep the browser pool utilized - you're hitting the `browsers.create()` rate limit at volume From eebfed78e489a690f03a7a3d542b0a1ec510e2ad Mon Sep 17 00:00:00 2001 From: dprevoznik <58714078+dprevoznik@users.noreply.github.com> Date: Mon, 28 Sep 2026 19:43:04 +0000 Subject: [PATCH 13/14] Move rate limit bullet up in pool guidance Co-Authored-By: Claude Opus 5.5 --- introduction/scale.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/introduction/scale.mdx b/introduction/scale.mdx index 98739bb7..6baed5ba 100644 --- a/introduction/scale.mdx +++ b/introduction/scale.mdx @@ -17,9 +17,9 @@ Stick with **on-demand `browsers.create()`** when: Reach for a **browser pool** when: - you've built and scaled your workload, and every run uses the same workload attributes +- you're hitting the `browsers.create()` rate limit at volume - you need the lowest possible acquisition latency (for example, a heavily customized browser config) - traffic is steady or high-frequency enough to keep the browser pool utilized -- you're hitting the `browsers.create()` rate limit at volume If you're on an Enterprise plan, speak with your account manager about applicable rate limits for `browsers.create()` and what's best for your workloads. From ff8f3d8b0f8cf62e61cbc3d61962870f9fdb70ac Mon Sep 17 00:00:00 2001 From: dprevoznik <58714078+dprevoznik@users.noreply.github.com> Date: Mon, 28 Sep 2026 19:47:30 +0000 Subject: [PATCH 14/14] Recommend on-demand browsers as the default in scale guide Co-Authored-By: Claude Opus 5.5 --- introduction/scale.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/introduction/scale.mdx b/introduction/scale.mdx index 6baed5ba..e3f7e1a7 100644 --- a/introduction/scale.mdx +++ b/introduction/scale.mdx @@ -8,6 +8,8 @@ This guide covers how to run Kernel in production at scale — whether to create ## Should I use on-demand browsers or a browser pool? +We recommend defaulting to on-demand browsers, both when you're getting started and as you scale. Browser pools fit a specific type of workload, described below. + Stick with **on-demand `browsers.create()`** when: - you're still building