Skip to content
Draft
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
14 changes: 9 additions & 5 deletions browsers/pools/overview.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
---
title: "Overview"
description: "Pre-configure pools of reserved browsers"
description: "Pre-configure pools of warm, ready-to-use browsers"
---

Browser pools let you maintain a set of reserved, identical browsers ready for immediate use. Use them to set your preferred browser configuration in advance (such as stealth, proxies, extensions, and profiles), allowing you to minimize browser start-up latency and scale your workloads in production.
Browser pools let you maintain a set of identically-configured browsers ready for immediate use. Use them to set your preferred browser configuration in advance (such as stealth, proxies, extensions, and profiles), allowing you to minimize browser start-up latency and scale your workloads in production.

Acquiring a browser from a pool is faster than creating a browser directly. Reserved browsers and on-demand browsers share the same concurrency limit, and reserved browsers aren't billed until they're used. See [Scale](/introduction/scale) for how pools fit into best practices for production architecture.
Acquiring a browser from a pool is faster than creating a browser directly. Pooled browsers and on-demand browsers share the same concurrency limit, and browsers sitting idle in a pool aren't billed until they're used. See [Scale](/introduction/scale) for how pools fit into best practices for production architecture.

<Info>
New to pools? The [Quickstart](/browsers/pools/quickstart) walks through creating a pool and acquiring your first browser from it. This page is the full API surface.
</Info>

## How browser pools work

Expand Down Expand Up @@ -38,7 +42,7 @@ Browser pools are a way to pre-configure a fixed set of browsers without being c
</Steps>


## Create a pool of reserved browsers
## Create a pool

Create a browser pool with a specified size and configuration. All browsers in the pool share the same settings.

Expand Down Expand Up @@ -268,7 +272,7 @@ You have three ways to get an in-use browser onto the new configuration:

## Per-user profiles with pools

A profile attached to the pool config is [read-only](#create-a-pool-of-reserved-browsers) and shared by every browser, so it can't hold per-user login state across many users in a single pool. To use a pool's pre-warmed browsers to load many different per-user profiles **and** persist each user's state, attach the profile to the browser *after* you acquire it, then destroy the browser on release:
A profile attached to the pool config is [read-only](#create-a-pool) and shared by every browser, so it can't hold per-user login state across many users in a single pool. To use a pool's pre-warmed browsers to load many different per-user profiles **and** persist each user's state, attach the profile to the browser *after* you acquire it, then destroy the browser on release:

1. **Create the pool with no profile.** A profile can only be loaded into a browser that was created without one, so the pool must be profile-free.
2. **Acquire a browser** from the pool.
Expand Down
6 changes: 3 additions & 3 deletions browsers/pools/policy-json.mdx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
title: "Custom Chrome Policies"
description: "Customize Chrome behavior in reserved browser pools using Chrome policies"
description: "Customize Chrome behavior in browser pools using Chrome policies"
---

Browser pools accept an optional [`chrome_policy`](https://kernel.sh/docs/api-reference/browser-pools/create-a-browser-pool#body-chrome-policy) object that lets you apply [Chrome enterprise policies](https://chromeenterprise.google/policies/) to every browser in the pool. Use this to control startup behavior, default homepages, bookmarks, and other browser-level settings.

## Setting chrome policies

Pass a `chrome_policy` object when [creating](/browsers/pools/overview#create-a-pool-of-reserved-browsers) or [updating](/browsers/pools/overview#update-a-pool) a pool. Keys are Chrome policy names and values are the corresponding settings.
Pass a `chrome_policy` object when [creating](/browsers/pools/overview#create-a-pool) or [updating](/browsers/pools/overview#update-a-pool) a pool. Keys are Chrome policy names and values are the corresponding settings.

<CodeGroup>
```typescript Typescript/Javascript
Expand Down Expand Up @@ -176,7 +176,7 @@ The example above demonstrates setting a default homepage and managed bookmarks.

## Common use cases

`chrome_policy` is also accepted directly on `browsers.create()` when you don't need a reserved pool. The examples below use that path.
`chrome_policy` is also accepted directly on `browsers.create()` when you don't need a pool. The examples below use that path.

### Allow pop-ups

Expand Down
280 changes: 280 additions & 0 deletions browsers/pools/quickstart.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
---
title: "Browser Pools Quickstart"
sidebarTitle: "Quickstart"
description: "Create a pool of pre-warmed browsers and acquire your first one"
---

A browser pool keeps a set of identically-configured browsers warm and ready to use. Acquiring a browser from a pool is faster than creating one on demand because the browser has already booted with your stealth, proxy, extension, viewport, and profile settings applied.

**Idle browsers in a pool aren't billed.** You pay the standard per-second rate only while a browser is acquired and running, so a warm pool costs nothing between tasks. Pool capacity does count against your [concurrency limit](/info/pricing#concurrency-limits) — a pool of 20 uses 20 of your limit whether or not those browsers are acquired.

<Info>
Browser pools are available on the Start-Up and Enterprise plans. Install the Kernel SDK first:
- Typescript/Javascript: `npm install @onkernel/sdk`
- Python: `pip install kernel`
- Go: `go get github.com/kernel/kernel-go-sdk`
</Info>

## Create the pool

Create the pool once — at deploy time, on service startup, or by hand from the CLI or [dashboard](https://dashboard.onkernel.com). Keep it out of the code path that serves your workload: pools fill at 25% of their size per minute by default, so a pool of 20 takes about four minutes to warm up completely.

<CodeGroup>
```typescript Typescript/Javascript
import Kernel from '@onkernel/sdk';

const kernel = new Kernel();

const pool = await kernel.browserPools.create({
name: 'checkout-pool',
size: 20,
stealth: true,
timeout_seconds: 600,
});

console.log(pool.id);
```

```python Python
from kernel import Kernel

kernel = Kernel()

pool = kernel.browser_pools.create(
name="checkout-pool",
size=20,
stealth=True,
timeout_seconds=600,
)

print(pool.id)
```

```go Go
package main

import (
"context"
"fmt"

"github.com/kernel/kernel-go-sdk"
)

func main() {
ctx := context.Background()
client := kernel.NewClient()

pool, err := client.BrowserPools.New(ctx, kernel.BrowserPoolNewParams{
Name: kernel.String("checkout-pool"),
Size: 20,
Stealth: kernel.Bool(true),
TimeoutSeconds: kernel.Int(600),
})
if err != nil {
panic(err)
}

fmt.Println(pool.ID)
}
```

```bash CLI
kernel browser-pools create checkout-pool --size 20 --stealth --timeout 600
```
</CodeGroup>

Every browser in the pool gets the same configuration, so put whatever your workload needs here — proxies, extensions, a viewport, a profile, custom [Chrome policies](/browsers/pools/policy-json). See [Pool configuration options](/browsers/pools/overview#pool-configuration-options) for the full list.

## Acquire, drive, and release a browser

Now do the work. `acquire` returns a browser immediately if one is available, or waits until one is. The response carries the same fields as an on-demand browser — `session_id`, `cdp_ws_url`, `browser_live_view_url` — so your automation code doesn't change.

Release the browser when you're done. Release in a `finally` block: a browser you don't release stays acquired until its idle timeout expires, which shrinks the pool everyone else is acquiring from.

<CodeGroup>
```typescript Typescript/Javascript
import Kernel from '@onkernel/sdk';

const kernel = new Kernel();

const kernelBrowser = await kernel.browserPools.acquire('checkout-pool', {
acquire_timeout_seconds: 30,
});

try {
const response = await kernel.browsers.playwright.execute(
kernelBrowser.session_id,
{
code: `
await page.goto('https://www.onkernel.com');
return await page.title();
`,
},
);
console.log(response.result);
} finally {
await kernel.browserPools.release('checkout-pool', {
session_id: kernelBrowser.session_id,
});
}
```

```python Python
from kernel import Kernel

kernel = Kernel()

kernel_browser = kernel.browser_pools.acquire(
"checkout-pool",
acquire_timeout_seconds=30,
)

try:
response = kernel.browsers.playwright.execute(
id=kernel_browser.session_id,
code="""
await page.goto('https://www.onkernel.com')
return await page.title()
""",
)
print(response.result)
finally:
kernel.browser_pools.release(
"checkout-pool",
session_id=kernel_browser.session_id,
)
```

```go Go
package main

import (
"context"
"fmt"

"github.com/kernel/kernel-go-sdk"
)

func main() {
ctx := context.Background()
client := kernel.NewClient()

kernelBrowser, err := client.BrowserPools.Acquire(ctx, "checkout-pool", kernel.BrowserPoolAcquireParams{
AcquireTimeoutSeconds: kernel.Int(30),
})
if err != nil {
panic(err)
}
defer func() {
if err := client.BrowserPools.Release(ctx, "checkout-pool", kernel.BrowserPoolReleaseParams{
SessionID: kernelBrowser.SessionID,
}); err != nil {
panic(err)
}
}()

response, err := client.Browsers.Playwright.Execute(ctx, kernelBrowser.SessionID, kernel.BrowserPlaywrightExecuteParams{
Code: `
await page.goto('https://www.onkernel.com');
return await page.title();
`,
})
if err != nil {
panic(err)
}

fmt.Println(response.Result)
}
```

```bash CLI
kernel browser-pools acquire checkout-pool --timeout 30

# then, when you're done with the session it returned
kernel browser-pools release checkout-pool --session-id <session-id>
```
</CodeGroup>

By default, released browsers are reused: the browser goes back into the pool as-is and the next `acquire` gets it. Pass `reuse: false` to destroy it instead and have the pool warm a replacement — use that when the browser holds state you don't want the next task to see. See [Release a browser](/browsers/pools/overview#release-a-browser).

## Check on the pool

`available_count` tells you how many browsers are ready to be acquired right now, and `acquired_count` how many are in use.

<CodeGroup>
```typescript Typescript/Javascript
const pool = await kernel.browserPools.retrieve('checkout-pool');

console.log(pool.available_count, pool.acquired_count);
```

```python Python
pool = kernel.browser_pools.retrieve("checkout-pool")

print(pool.available_count, pool.acquired_count)
```

```go Go
pool, err := client.BrowserPools.Get(ctx, "checkout-pool")
if err != nil {
panic(err)
}

fmt.Println(pool.AvailableCount, pool.AcquiredCount)
```

```bash CLI
kernel browser-pools get checkout-pool
```
</CodeGroup>

Watch this number to size the pool. If `available_count` regularly hits 0, your tasks are queueing behind `acquire` and the pool needs to be bigger. If it stays above 30–40% of the pool size under normal load, you can shrink the pool and free up concurrency for other work. Target 10–20% available during typical load.

Resize at any time with `update`; there's no need to tear the pool down and rebuild it. Added capacity warms up at the pool's fill rate, so raise the size before a traffic peak rather than during it:

<CodeGroup>
```typescript Typescript/Javascript
await kernel.browserPools.update('checkout-pool', { size: 40 });
```

```python Python
kernel.browser_pools.update("checkout-pool", size=40)
```

```go Go
if _, err := client.BrowserPools.Update(ctx, "checkout-pool", kernel.BrowserPoolUpdateParams{
Size: 40,
}); err != nil {
panic(err)
}
```

```bash CLI
kernel browser-pools update checkout-pool --size 40
```
</CodeGroup>

## Before you go to production

Three things behave differently from on-demand browsers:

- **A pool's `timeout_seconds` only runs while a browser is acquired.** Browsers wait in the pool indefinitely; the clock starts when you acquire one and only counts idle time — no CDP connection, no live view, no in-flight computer controls request. A task that runs for 20 minutes with an active CDP connection won't be killed by a 10-minute timeout. See [Timeout behavior](/browsers/pools/overview#timeout-behavior).
- **Updating a pool doesn't reconfigure browsers that already exist.** `update` changes the configuration used for browsers created after it. Pass `discard_all_idle: true` to rebuild the idle ones too; acquired browsers keep their old configuration even then. See [Update a pool](/browsers/pools/overview#update-a-pool).
- **A profile set on the pool is read-only.** Every browser in the pool shares it, so `save_changes` doesn't apply at the pool level. To give each of your users their own persisted login state, attach the profile after you acquire the browser — see [Per-user profiles with pools](/browsers/pools/overview#per-user-profiles-with-pools).

## What's next

<Columns cols={2}>
<Card title="Overview" href="/browsers/pools/overview">
The full pool API — configuration options, profile refresh, flushing, and deletion.
</Card>
<Card title="Scale" href="/introduction/scale">
Architecture patterns for high concurrency, including pools behind a task queue.
</Card>
<Card title="Custom Chrome Policies" href="/browsers/pools/policy-json">
Apply Chrome enterprise policies — pop-up handling, download behavior, homepages — to every browser in a pool.
</Card>
<Card title="FAQ" href="/browsers/pools/faq">
Sizing, fill rate, reuse semantics, and debugging pooled browsers in production.
</Card>
</Columns>
17 changes: 9 additions & 8 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@
"browsers/termination",
"browsers/standby",
"browsers/headless",
{
"group": "Browser Pools",
"pages": [
"browsers/pools/quickstart",
"browsers/pools/overview",
"browsers/pools/policy-json",
"browsers/pools/faq"
]
},
"info/projects"
]
},
Expand Down Expand Up @@ -160,14 +169,6 @@
"browsers/telemetry/categories",
"browsers/telemetry/streaming"
]
},
{
"group": "Reserved Browsers",
"pages": [
"browsers/pools/overview",
"browsers/pools/policy-json",
"browsers/pools/faq"
]
}
]
},
Expand Down
2 changes: 1 addition & 1 deletion index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,4 @@ kernel invoke my-agent my-task --payload '{"url": "https://example.com"}'

### scaling

once you're ready to scale, check out how to create a [pool of reserved browsers](/browsers/pools/overview).
[browser pools](/browsers/pools/quickstart) keep browsers warm and pre-configured so you skip start-up latency on every task. idle pooled browsers aren't billed, so most production workloads run on them from day one.
2 changes: 2 additions & 0 deletions info/concepts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ title: "Concepts"
## Browser
A `Browser` is a cloud-based browser managed by Kernel. They accept Chrome DevTools Protocol connections and can be used to run browser automations or web agents.

## Browser Pool
A `Browser Pool` is a set of identically-configured browsers that Kernel keeps warm and ready to use. You acquire a browser from the pool when a task starts and release it back when the task finishes. Pools remove browser start-up latency from your workload — see [Browser Pools](/browsers/pools/quickstart).

## App
An `App` is a codebase deployed on Kernel. You can use Kernel for a variety of use cases, including web automations, data processing, and more.
Expand Down
Loading
Loading