Overview
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 and controlling browsers; for the mechanics of standing up a pool and acquiring from it, see Browser Pools.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-demandbrowsers.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)
- 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
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 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 on a fresh browser), so
acquirehands 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 on
browsers.create()that high-volume workloads hit.
Sizing
Watchavailable_count and target 10–20% available under normal load, resizing before traffic peaks rather than during them. See Sizing a browser pool for the full guidance.
Architecture patterns
On-demand creation
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:- Early development and testing
- Configuration that changes per user or per task
- GPU browsers
Example
Example
Single browser pool
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 thebrowsers.create() rate limit.
When to use:
- Consistent, high-frequency workloads on a fixed configuration
- Steady request patterns, or latency-sensitive acquisition
Example
Example
- Pool size should match your typical concurrency
- Always release browsers in a
finallyblock to prevent browser pool exhaustion - Set
acquire_timeout_secondsbased on your SLA requirements
Queue-based processing
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, withbrowsers.create() in place of acquire and deleteByID in place of release.
When to use:
- Request volume exceeds your available concurrency
- Highly variable traffic patterns
- Need to prioritize certain tasks
- Want to decouple request ingestion from processing
Example
Example
- Set worker concurrency to match or slightly exceed browser pool size
- Implement proper retry logic for transient failures
- Monitor queue depth to scale browser pools dynamically
- Use priority queues for different SLAs