Skip to main content

When should I use browser pools vs. creating browsers on-demand?

Use browser pools when you need to run many browsers concurrently (50+ simultaneous browsers). Pools allow you to pre-configure a set of browsers for your use case, enabling you to scale to hundreds or thousands of concurrent browser sessions.

How do I know if my pool is too small or too large?

Monitor the available_count metric. If it frequently drops to 0, your pool is too small. If it stays above 30-40% of the pool size during normal operation, you’re over-provisioned. Target 10-20% available browsers during typical load.

What happens if I don’t release a browser back to the pool?

The browser will remain “acquired” until the idle timeout expires, at which point it’s destroyed and a new browser is created to refill the pool. Unreleased browsers create inefficiency and can exhaust your pool.

What is fill_rate_per_minute?

fill_rate_per_minute is the percentage of the pool that will be refilled per minute. For example, if you set fill_rate_per_minute to 10, the pool will be refilled with 10% of the pool size per minute. When a browser from a pool is set to be destroyed (by reaching its specified timeout_seconds or reuse: false), a pool refill will be triggered to replace any destroyed browsers with new ones. Refill checks run once per minute.

Can I update a pool’s configuration without recreating it?

Yes, use kernel.browserPools.update(). By default (discard_all_idle: false), existing idle browsers keep their current configuration and only newly created browsers use the new one. Pass discard_all_idle: true to discard all idle browsers and rebuild them immediately with the new configuration.

If I update a pool, do browsers that are currently in use pick up the new configuration?

No. A plain update() doesn’t rebuild any existing browsers — it only changes the configuration used for future ones. Even the idle browsers keep their old config unless you pass discard_all_idle: true (or flush the pool). Browsers that are acquired during the update are never touched regardless: an in-use browser keeps its original configuration, and if you release it with reuse: true (the default) it returns to the pool still running the old configuration and keeps getting handed out that way. You have three ways to get it onto the new configuration: release it with reuse: false so it’s destroyed and rebuilt on release instead of the old one returning to the pool; let the acquired browser reach its timeout_seconds while idle so it’s destroyed and the pool refills automatically; or flush it after the fact with kernel.browserPools.flush() (or a later kernel.browserPools.update() with discard_all_idle: true) once the in-use browsers have been released.

If I update a profile’s contents, will my pool’s idle browsers pick up the change?

By default, yes — refresh_on_profile_update is automatically set to true when a pool is created with a profile or when the pool’s profile is changed. Idle browsers are flushed whenever the profile is saved, so they get replaced with fresh ones using the updated profile. If you’ve explicitly disabled refresh_on_profile_update, you can still force the pool to pick up new profile contents:
  • Manual: Call kernel.browserPools.flush() to destroy idle browsers (the pool refills automatically), or call kernel.browserPools.update() with discard_all_idle: true. See Refresh on profile update.

Can pooled browsers save changes back to a profile?

No. A profile attached to a pool is loaded read-only — pooled browsers never persist changes back to the profile, so save_changes does not apply to pools. Sending save_changes on a pool’s profile is silently ignored (it is not rejected), so reusing a single-session profile object won’t error. To capture profile state, use a single browser session instead: kernel.browsers.create({ profile: { name, save_changes: true } }).

Should I set reuse: true or reuse: false when releasing?

Use reuse: true (default) for efficiency. Only use reuse: false when you suspect browser state corruption or need a guaranteed clean browser session.

How do pool timeouts interact with task duration?

The pool’s timeout_seconds only applies while the browser is acquired. If your task takes 5 minutes and timeout is 3 minutes, the browser won’t be destroyed—the timeout only triggers if the browser is idle (no CDP connection) for 3 minutes.

Can I use different browser configurations within the same pool?

All browsers in a pool initialize with the same configuration. Calling kernel.browserPools.update() changes the pool’s configuration for browsers created after the update; existing idle browsers keep their original configuration unless you pass discard_all_idle: true (or flush the pool). Once you’ve acquired a browser, you can apply certain hot swap configurations to that browser instance using kernel.browsers.update().

How do I handle rate limiting from target websites?

  • Implement delays between requests
  • Use multiple proxy pools to distribute requests across IPs
  • Implement backoff when rate limits are detected
  • Consider creating separate pools with different proxy configurations

What’s the best way to debug issues in production?

  • Use headless: false for a temporary debug pool, access browser_live_view_url to watch browser sessions in real-time
  • Collect screenshots at error points, and maintain detailed logging around acquire/release operations
  • Monitor past sessions via replays.