← Back to posts
comparison guide self-hosted · · 9 min read

Self-Hosted Browser Automation Servers: Browserless vs Playwright vs Selenium Grid 2026

Compare self-hosted browser automation servers — Browserless, Playwright Server, and Selenium Grid. Learn to deploy headless Chrome, Firefox, and WebKit with Docker for web scraping, testing, and automation at scale.

OS
Editorial Team

Running browser automation at scale requires more than just a testing framework — you need reliable browser infrastructure. Whether you’re scraping websites, running end-to-end tests, or automating repetitive web tasks, having a self-hosted browser server gives you full control over your automation pipeline without depending on expensive cloud services.

This guide compares the three most popular self-hosted browser automation server solutions: Browserless, Playwright’s built-in server, and Selenium Grid. We’ll cover deployment options, configuration, scaling capabilities, and real-world use cases to help you pick the right tool.

Why Self-Host Your Browser Automation

Commercial browser automation services like BrowserStack, Sauce Labs, and commercial Browserless tiers charge per session or per minute. When you’re running hundreds of automated tests daily, doing large-scale web scraping, or processing PDFs and screenshots in bulk, those costs add up fast.

Self-hosting your browser automation infrastructure gives you:

  • Unlimited sessions — no per-minute billing, run as many concurrent browsers as your hardware supports
  • Data privacy — all browsing happens on your own servers, no third-party visibility
  • Full control — install custom extensions, configure network settings, set custom user agents
  • Cost predictability — a single $20/month VPS can replace hundreds of dollars in cloud browser fees
  • Integration flexibility — connect to your existing CI/CD, monitoring, and orchestration tools

For related reading, see our E2E testing tools comparison for the frameworks that connect to these servers, and our web scraping guide for end-to-end scraping architectures.

Browserless: Managed Headless Browsers as a Service

Browserless (12,976 GitHub stars, last updated April 2026) is the most popular turnkey self-hosted browser server. It provides a REST API and WebSocket interface for controlling headless Chrome, with built-in session management, queueing, and resource limits.

Key Features

  • REST API for browser control alongside WebSocket CDP (Chrome DevTools Protocol) connections
  • Built-in concurrency limiting and session queuing to prevent resource exhaustion
  • Automatic session timeout and cleanup for orphaned browser instances
  • Pre-installed fonts and Chrome extensions ready out of the box
  • docker Compose deployment with health checks
  • Puppeteer and Playwright compatible APIs
  • PDF generation and screenshot endpoints built in

Docker Compose Deployment

The simplest way to run Browserless is with Docker Compose:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
services:
  browserless:
    image: ghcr.io/browserless/chromium:latest
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - TOKEN=your-secret-api-token
      - CONCURRENT=10
      - QUEUE_LENGTH=10
      - TIMEOUT=60000
      - HEALTH=true
    volumes:
      - ./data:/usr/src/app/data

Start it with:

1
docker compose up -d

API Usage Example

Once running, you can connect via Puppeteer:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const puppeteer = require('puppeteer-core');

(async () => {
  const browser = await puppeteer.connect({
    browserWSEndpoint: 'ws://localhost:3000?token=your-secret-api-token',
  });
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({ path: 'screenshot.png' });
  await browser.disconnect();
})();

Or use the REST API for screenshots directly:

1
2
3
4
curl -X POST http://localhost:3000/screenshot \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "options": {"fullPage": true}}' \
  --output screenshot.png

Browserless also supports Playwright connections:

1
2
3
4
5
const { chromium } = require('playwright');

const browser = await chromium.connectOverCDP(
  'http://localhost:3000?token=your-secret-api-token'
);

Playwright: Built-in Browser Server

Playwright (86,743 GitHub stars, last updated April 2026) is Microsoft’s cross-browser automation framework. While primarily known as a testing library, Playwright includes a built-in browser server mode (playwright run-server) that turns any machine into a remote browser automation endpoint.

Key Features

  • Native support for Chromium, Firefox, and WebKit from a single API
  • Built-in server mode for remote browser connections
  • Auto-wait and intelligent actionability checks
  • Network interception and mocking capabilities
  • Trace viewer for debugging failed sessions
  • Multi-language support: TypeScript, Python, Java, C#
  • No additional infrastructure needed — the framework itself is the server

Docker Compose Deployment

Playwright provides official Docker images that include all three browser engines:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
services:
  playwright-server:
    image: mcr.microsoft.com/playwright:v1.52.0-jammy
    restart: unless-stopped
    ports:
      - "3001:3001"
    command: >
      npx -y playwright@latest run-server --port 3001 --host 0.0.0.0
    environment:
      - PLAYWRIGHT_BROWSERS_PATH=/ms-playwright
    volumes:
      - playwright-data:/ms-playwright

volumes:
  playwright-data:

Start with:

1
docker compose up -d

Connecting to Playwright Server

From a client machine, connect to the remote server:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.connect('http://your-server:3001');
  const page = await browser.newPage();
  await page.goto('https://example.com');
  const title = await page.title();
  console.log('Page title:', title);
  await browser.close();
})();

For Python users:

1
2
3
4
5
6
7
8
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.connect("http://your-server:3001")
    page = browser.new_page()
    page.goto("https://example.com")
    print(page.title())
    browser.close()

Selenium Grid: Distributed Browser Automation at Scale

Selenium (34,083 GitHub stars, last updated April 2026) is the original browser automation framework. Selenium Grid extends it by providing a hub-and-node architecture for distributing browser sessions across multiple machines, making it ideal for large-scale parallel testing.

Key Features

  • Hub-and-node architecture for horizontal scaling
  • Support for Chrome, Firefox, Edge, and Safari
  • Distributed session routing across multiple nodes
  • Built-in Docker Compose configuration
  • Language bindings for Java, Python, JavaScript, Ruby, C#
  • Mature ecosystem with thousands of integrations
  • Session queue and max session configuration

Docker Compose Deployment

Selenium provides an official Docker Compose setup with separate hub and node containers:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
services:
  selenium-hub:
    image: selenium/hub:4.30.0
    ports:
      - "4442-4444:4442-4444"
      - "7900:7900"
    environment:
      - SE_VNC_NO_PASSWORD=1

  chrome-node:
    image: selenium/node-chrome:4.30.0
    depends_on:
      - selenium-hub
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - SE_NODE_MAX_SESSIONS=4
    shm_size: '2gb'

  firefox-node:
    image: selenium/node-firefox:4.30.0
    depends_on:
      - selenium-hub
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - SE_NODE_MAX_SESSIONS=4
    shm_size: '2gb'

Start the full grid:

1
docker compose up -d

Connecting to Selenium Grid

Use the standard Selenium WebDriver to connect:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from selenium import webdriver
from selenium.webdriver.common.by import By

options = webdriver.ChromeOptions()
driver = webdriver.Remote(
    command_executor='http://localhost:4444/wd/hub',
    options=options
)

driver.get('https://example.com')
print(driver.title)
driver.quit()

Feature Comparison

FeatureBrowserlessPlaywright ServerSelenium Grid
BrowsersChromium onlyChromium, Firefox, WebKitChrome, Firefox, Edge, Safari
API StyleREST + WebSocket CDPWebSocketWebDriver Protocol
ConcurrencyConfigurable limitsManual managementHub-managed with queues
Docker ImageSingle containerSingle containerMulti-container (hub + nodes)
Session QueueBuilt-inNo (manual)Built-in
LanguagesAny (CDP-based)TS, Python, Java, C#Java, Python, JS, Ruby, C#
ScalingVertical (more resources)VerticalHorizontal (more nodes)
Resource LimitsTOKEN, CONCURRENT, TIMEOUTManualSE_NODE_MAX_SESSIONS
VNC AccessNoNoYes (built-in)
GitHub Stars~12,976~86,743~34,083
Best ForQuick deployment, APIsModern testing, cross-browserLarge-scale parallel testing

Choosing the Right Solution

Pick Browserless If:

  • You want the fastest path to a running browser server
  • Your workflow is Chromium-only (most web scraping and PDF generation)
  • You need a REST API in addition to WebSocket connections
  • You want built-in concurrency limiting and session management
  • You’re running screenshot or PDF generation services

Browserless is the easiest to deploy and manage. The single-container setup with environment-based configuration makes it ideal for teams that want a “just works” solution without managing complex infrastructure.

Pick Playwright Server If:

  • You need cross-browser coverage (Chromium + Firefox + WebKit)
  • Your team already uses Playwright for testing
  • You want the most modern API with auto-wait and intelligent actions
  • You need network interception and request mocking
  • You prefer a lighter-weight setup than Selenium Grid

Playwright’s server mode is the best middle ground — it gives you the power of Playwright’s cross-browser support without the complexity of a multi-node grid.

Pick Selenium Grid If:

  • You need horizontal scaling across multiple machines
  • You’re running hundreds of parallel test sessions
  • You need Safari support (only Selenium supports it)
  • You want VNC access to browser sessions for debugging
  • Your existing codebase is built on Selenium WebDriver

Selenium Grid remains the go-to choice for large-scale test infrastructure. The hub-and-node architecture lets you add capacity by spinning up more node containers, making it the only truly horizontally scalable option.

Resource Planning

For a self-hosted browser server, plan your resources based on expected concurrency:

Concurrent SessionsMinimum RAMRecommended CPUStorage
2–44 GB2 cores10 GB
5–108 GB4 cores20 GB
10–2016 GB8 cores40 GB
20+32 GB+16 cores+80 GB+

Each headless browser instance consumes 200–500 MB of RAM depending on the pages loaded. Chromium-based browsers are generally lighter than Firefox.

Adding Reverse Proxy with TLS

For production deployments, put your browser server behind a reverse proxy:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
services:
  browserless:
    image: ghcr.io/browserless/chromium:latest
    environment:
      - TOKEN=${BROWSER_TOKEN}
[nginx](https://nginx.org/) - CONCURRENT=5
    expose:
      - "3000"

  nginx:
    image: nginx:alpine
    ports:
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      - browserless

With an Nginx configuration that adds rate limiting:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
limit_req_zone $binary_remote_addr zone=browser:10m rate=10r/s;

server {
    listen 443 ssl;
    server_name browser.yourdomain.com;

    ssl_certificate /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.pem;

    location / {
        limit_req zone=browser burst=20 nodelay;
        proxy_pass http://browserless:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}

FAQ

Can I run multiple browser types on a single server?

Yes, but the approach depends on your tool. Selenium Grid supports Chrome, Firefox, Edge, and Safari nodes on the same hub — you simply add more node containers. Playwright Server supports Chromium, Firefox, and WebKit but each server instance runs all three. Browserless only supports Chromium; if you need Firefox you’d need to run a separate instance or use a different tool.

How many concurrent sessions can a single server handle?

This depends on your hardware. As a rule of thumb, each headless browser uses 200–500 MB of RAM. A 16 GB server can comfortably run 10–20 concurrent sessions. Browserless lets you set the CONCURRENT environment variable to enforce limits, while Selenium Grid uses SE_NODE_MAX_SESSIONS per node.

Is self-hosted browser automation secure?

Yes, as long as you follow best practices: set a TOKEN or authentication mechanism, never expose the server directly to the internet without a reverse proxy, use TLS encryption, and implement rate limiting. Browserless supports token-based auth, and you should always use a reverse proxy like Nginx or Traefik for production deployments.

Can I use these servers with Puppeteer?

Browserless has native Puppeteer support via puppeteer-core.connect(). Playwright can connect via CDP (Chrome DevTools Protocol) which Puppeteer also uses. Selenium Grid does not directly support Puppeteer — you’d need to use Selenium’s WebDriver API or a compatibility layer like puppeteer-webdriver.

What’s the difference between Browserless and the commercial Browserless cloud?

The open-source Browserless server is free and self-hosted. The commercial cloud service adds managed infrastructure, auto-scaling, persistent sessions, and support. For most self-hosting use cases, the open-source version provides all the core functionality you need.

How do I monitor browser server health?

Browserless has a /health endpoint that returns session counts and resource usage. Selenium Grid provides a web UI at port 7900 (VNC) and a status API at http://host:4444/status. For Playwright Server, you’ll need to implement custom health checks or use Docker’s built-in health monitoring.

Advertise here