selenium-grid-4-runner
Author and operate Selenium Grid 4 - self-hosted distributed WebDriver. Covers the six-component architecture (Router / Distributor / Session Map / Event Bus / New Session Queue / Node), standalone vs hub-and-node modes, the Docker-image stack (selenium/standalone-chrome, selenium/hub, selenium/node-chrome), node registration, session-queue tuning, and observability. Use for self-hosted cross-browser testing when data residency or cost-control require an on-prem grid. This is the self-hosted execution RUNNER - for the zero-infra alternative use browser-matrix-runner (Playwright bundled engines); for managed cloud grids use browserstack-automate, saucelabs-automate, or lambdatest-automate; to decide WHICH browsers and tiers to run use browser-matrix-strategy-reference.
Install with skills.sh (any agent)
npx skills add testland/qa --skill selenium-grid-4-runnerselenium-grid-4-runner
Overview
Selenium Grid 4 is the self-hosted distributed WebDriver infrastructure - the open-source alternative to cloud-grid SaaS providers. Per selenium.dev/documentation/grid (opens in new window).
Composes with browser-matrix-strategy-reference for matrix planning.
When to use
For cloud-hosted alternatives see browserstack-automate, saucelabs-automate, lambdatest-automate.
Authoring
Six-component architecture
| Component | Role |
|---|---|
| Router | Entry point; routes WebDriver requests to the right session |
| Distributor | Allocates new sessions to available Nodes based on capabilities |
| Session Map | Tracks active sessions (session ID → Node URL) |
| Event Bus | Internal messaging between components |
| New Session Queue | Holds pending session requests when no Node available |
| Node | Runs actual browser instances; registers with the Distributor |
Standalone mode (development / small teams)
All components in one JVM:
# Download from selenium.dev/downloads
java -jar selenium-server-<version>.jar standaloneDefault port 4444. WebDriver clients connect to http://localhost:4444/wd/hub.
Standalone mode is suitable for a single developer's machine or a small CI runner with co-located browsers.
Hub-and-node mode (production)
Hub on one machine, Nodes on others:
# Hub
java -jar selenium-server-<version>.jar hub
# Node (on another machine)
java -jar selenium-server-<version>.jar node \
--hub http://hub-host:4444 \
--port 5555For very large deployments each of the six components can run as its own process; see references/distributed-and-ci.md. Most teams run hub-and-node.
Docker stack
Docker images are published as selenium/* on Docker Hub. Pin one Grid version across every image via a single SE_VERSION variable (examples use 4.21.0); keep it identical on the Hub and all Nodes.
# docker-compose.yml (set SE_VERSION=4.21.0 in .env or the environment)
services:
selenium-hub:
image: selenium/hub:${SE_VERSION}
ports: ["4442:4442", "4443:4443", "4444:4444"]
chrome:
image: selenium/node-chrome:${SE_VERSION}
shm_size: 2gb
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=2
firefox:
image: selenium/node-firefox:${SE_VERSION}
shm_size: 2gb
depends_on: [selenium-hub]
environment:
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_PUBLISH_PORT=4442
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
edge:
image: selenium/node-edge:${SE_VERSION}
shm_size: 2gb
depends_on: [selenium-hub]
environment:
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_PUBLISH_PORT=4442
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443shm_size: 2gb is required for Chrome (shared-memory bloat with many tabs).
Standalone Docker (simpler)
docker run -d -p 4444:4444 -p 7900:7900 --shm-size="2g" \
selenium/standalone-chrome:${SE_VERSION}Port 7900 exposes noVNC (browser at http://localhost:7900) for live-session viewing.
Running
Connect a WebDriver client
from selenium import webdriver
driver = webdriver.Remote(
command_executor="http://grid-router:4444/wd/hub",
options=webdriver.ChromeOptions(),
)
driver.get("https://example.com")
# ...
driver.quit()For Kubernetes-deployed Grid use the cluster-internal DNS: http://selenium-router.test-ns.svc.cluster.local:4444/wd/hub.
Capabilities
Standard W3C - no grid-specific options needed:
{
"browserName": "chrome",
"browserVersion": "stable",
"platformName": "linux"
}Session-queue tuning
Key knobs:
| Setting | Effect |
|---|---|
--session-request-timeout | How long a queued session waits before failing (default 300s) |
--session-retry-interval | Polling interval for matching capabilities (default 5s) |
SE_NODE_MAX_SESSIONS | Max concurrent sessions per Node (default 1) |
SE_NODE_SESSION_TIMEOUT | Inactive session cleanup (default 300s) |
Tune SE_NODE_MAX_SESSIONS per Node's CPU + memory budget; typical: 2 Chrome / 1 Firefox per 2-core / 4 GB Node.
Observability
Grid 4 exposes:
For production add a Grafana dashboard polling Prometheus.
Parsing results
Grid 4 doesn't add session videos / HAR by default - that's the test client's responsibility (or via a sidecar like selenoid + selenoid-ui for Grid 3-style recording).
Logs at /var/log/seluser/ inside Docker containers, exportable via volume mount.
CI integration
Boot the grid, gate on /status ready before running tests, and always tear down. Full GitHub Actions workflow: references/distributed-and-ci.md.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
shm_size default (64 MB) on Chrome node | Chrome crashes mid-session | Always shm_size: "2g" |
SE_NODE_MAX_SESSIONS too high | OOM / CPU thrashing | Conservative: 2 per 2-core Node |
| Hub-and-node without health check | Failed nodes silently drop sessions | Wait for /status ready before tests start |
| Standalone in production | No HA; single point of failure | Hub-and-node minimum for prod |
| Manually allocating ports for Nodes | Conflicts | Let Docker assign + use service DNS |
| No session-queue timeout | Sessions wait forever; CI hangs | Set --session-request-timeout bounded |
| Mixing Selenium versions across Hub + Nodes | Capability negotiation breaks | Pin same Grid version across all components |
Limitations
References
Selenium Grid 4 - fully-distributed layout and CI
View source (opens in new window)Selenium Grid 4 - fully-distributed layout and CI
Deep-dive extensions to selenium-grid-4-runner. Standalone and hub-and-node (both in the parent SKILL.md) cover almost every team; reach for these only when the grid must scale past a single hub or run in CI.
Fully-distributed mode (all components separated)
For very large deployments, run each of the six components as its own process instead of one hub JVM. Most teams do NOT need this - hub-and-node is the default.
# Event Bus
java -jar selenium-server.jar event-bus --port 5557
# New Session Queue
java -jar selenium-server.jar sessionqueue --port 5559
# Session Map
java -jar selenium-server.jar sessions --port 5556
# Distributor
java -jar selenium-server.jar distributor --port 5553 \
--sessions http://sessions-host:5556 \
--sessionqueue http://queue-host:5559 \
--bind-bus-events false \
--publish-events tcp://event-bus-host:4442 \
--subscribe-events tcp://event-bus-host:4443
# Router
java -jar selenium-server.jar router --port 4444 \
--sessions http://sessions-host:5556 \
--distributor http://distributor-host:5553 \
--sessionqueue http://queue-host:5559
# Node(s)
java -jar selenium-server.jar node \
--publish-events tcp://event-bus-host:4442 \
--subscribe-events tcp://event-bus-host:4443CI integration (GitHub Actions)
Boot the grid, gate on /status ready before running tests, and always tear down:
on: pull_request
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Start Selenium Grid
run: docker-compose -f selenium-grid.yml up -d
- name: Wait for Grid ready
run: |
for i in {1..30}; do
curl -s http://localhost:4444/wd/hub/status | grep -q '"ready":true' && break
sleep 2
done
- name: Run E2E tests
run: pytest tests/e2e/ --grid-url=http://localhost:4444/wd/hub
- name: Tear down Grid
if: always()
run: docker-compose -f selenium-grid.yml downRelated skills
browser-matrix-runner
Configures a CI matrix that runs the smoke / regression suite across multiple browsers per Playwright's three-engine support (Chromium, Firefox, WebKit / Safari) plus branded variants (chrome, msedge channels). Wires GitHub Actions / GitLab CI matrix syntax, captures per-browser screenshots, and aggregates per-browser pass/fail. Use when the product targets multiple browsers and the team wants automated cross-browser testing or browser-compatibility regression - e.g. a 'works in Chrome, broken in Safari' bug that needs Chrome / Firefox / Safari coverage.
browser-matrix-strategy-reference
Pure-reference for designing and reviewing a browser / OS / device test matrix from traffic data - the T1/T2/T3 tier-membership heuristics (T1 >=5% traffic, T2 1-5% or statutory, T3 <1% with customer demand), the traffic-share sources (own analytics, StatCounter, MDN browser-compat-data), a worked matrix template with tier-change log, and how to justify dropping a legacy browser (IE11, old iOS Safari). Use when designing an initial matrix, running a quarterly re-tier review, or making the case to drop a browser. This is the WHAT-to-test strategy reference - to execute the matrix use the runners browser-matrix-runner (bundled engines) or selenium-grid-4-runner (self-hosted); to cap and publish committed support tiers use compatibility-budget.
compatibility-budget
Pure-reference for deciding how large a compatibility matrix a team can afford and for publishing that commitment - defines tier-1 (must work; per-PR) vs tier-2 (must work; nightly) vs tier-3 (should work; pre-release) vs unsupported, with example budgets per product type (web / desktop / mobile / library), the matrix-size cost / coverage trade-off, and 'what we support' external templates. Use when a team must cap how many browser / OS / runtime combos it commits to, or must publish a support policy. This is the BUDGET and support-statement gate - for the traffic-share analysis that picks WHICH specific browsers belong in each tier use browser-matrix-strategy-reference; to execute the resulting matrix use the runners browser-matrix-runner (bundled engines) or selenium-grid-4-runner (self-hosted).
os-matrix-runner
Configures a CI matrix that runs tests across operating systems (Linux / macOS / Windows) and runtime versions (Node 18/20/22; Python 3.10/3.11/3.12; Java 17/21; .NET 6/8). Wires GitHub Actions matrix syntax, addresses OS-specific quirks (path separators, line endings, file permissions). Use when the product ships across OS / runtime combinations and the team needs continuous cross-platform coverage.