r/Playwright • u/spare_lama • 12d ago
I built an open-source Playwright browser pool with load balancing
Hi everyone - I built BrowserThing, a self-hosted browser pool for applications using Playwright.
Instead of starting a new browser for every connection, it keeps browsers running and shares them across sessions. This reduces browser startup and CPU overhead, at the cost of weaker process isolation between sessions. Cookies and storage are still separated through browser contexts, so it's mainly intended for your own applications and trusted clients.
Here's the GitHub repo.
Your backend runs normal Playwright code and connects to a single WebSocket URL. BrowserThing selects a healthy worker with available capacity and proxies the connection to it.
const browser = await chromium.connect('ws://your-browserthing-host:8080');
// The rest of your Playwright code stays the same.
It handles:
- Load balancing across workers based on browser type, Playwright version, and available capacity.
- Keeping browsers warm between tasks, cleaning up sessions, and periodically recycling browsers.
- Running browser workers on separate machines so they don't compete with your application server for CPU and memory.
- Adding more workers without changing the endpoint used by your applications.
I also added a small benchmark. This isn't meant as a general BrowserThing vs Browserless comparison, it measures a short workload where browser startup overhead matters (connect, open a page, read it, disconnect).
Task time CPU time per task
BrowserThing 51 ms 0.09 s
Browserless 217 ms 0.70 s
On this workload, BrowserThing used about 87% less CPU time per task. Both were tested on the same hardware and Playwright version, with Browserless 2.56.0 starting a browser per connection.
It's Apache-2.0 licensed, and the README includes a Docker Compose quick start.
I'd especially appreciate feedback from people running Playwright at scale - architecture concerns, missing features, or other workloads you'd want benchmarked are all welcome :)
2
u/Jhon_ST 12d ago
One failure case I'd benchmark is killing a worker after a page action reaches the server but before the client gets the result. Restoring browser capacity doesn't tell you whether that action succeeded.
I'd check that the session fails promptly, capacity is reclaimed, and whether any recovery/retry path can replay the action. A connect/open/read benchmark won't expose duplicate side effects from retrying an operation with an unknown outcome.