Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion browsers/pools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion info/pricing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ The [app invocation limits](#concurrency-limits) are concurrency limits, not a n
<Accordion title="How are browser pools charged?">
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.
</Accordion>
<Accordion title="How is Managed Auth charged?">
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.
Expand Down
50 changes: 6 additions & 44 deletions introduction/create.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -89,51 +89,13 @@ 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. It's the default while you're building, and it stays the right fit when your configuration changes per user as you scale.

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.
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.

<CodeGroup>
```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
```
</CodeGroup>

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).
<Info>
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.
</Info>

## Lifecycle

Expand Down Expand Up @@ -264,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.
59 changes: 32 additions & 27 deletions introduction/scale.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,53 @@ 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
## Should I use on-demand browsers or 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:
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.

- **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.
- **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.
Stick with **on-demand `browsers.create()`** when:

## When to use a pool vs on-demand
- 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're running the same workload repeatedly, in production
- 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'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

Stick with **on-demand `browsers.create()`** when:
<Info>
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.
</Info>

- volume is low, bursty, one-off, or you're still developing
- each session needs a different configuration (a pool is one fixed config)
- you need a GPU browser (not available in pools)
## What a browser pool gives you

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.
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

Watch `available_count` and target 10–20% available under normal load, resizing before traffic peaks rather than during them. See [Sizing a browser pool](/browsers/pools#sizing-a-browser-pool) for the full guidance.

## 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

<Accordion title="Example">

Expand Down Expand Up @@ -84,9 +88,9 @@ async function processTask(taskData: any) {
```
</Accordion>

### 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
Expand Down Expand Up @@ -151,21 +155,22 @@ 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

<Accordion title="Example">

```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';
Expand Down
Loading