Testland
Browse all skills & agents

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-runner
View source

selenium-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

  • Data-residency requirements forbid cloud grids.
  • Cost: high-volume testing where cloud per-session pricing exceeds self-hosting break-even.
  • Internal-network apps where tunnel solutions (BrowserStackLocal / Sauce Connect) are insufficient.
  • Custom browser builds / non-standard configurations cloud grids don't support.

For cloud-hosted alternatives see browserstack-automate, saucelabs-automate, lambdatest-automate.

Authoring

Six-component architecture

ComponentRole
RouterEntry point; routes WebDriver requests to the right session
DistributorAllocates new sessions to available Nodes based on capabilities
Session MapTracks active sessions (session ID → Node URL)
Event BusInternal messaging between components
New Session QueueHolds pending session requests when no Node available
NodeRuns 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 standalone

Default 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 5555

For 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=4443

shm_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:

SettingEffect
--session-request-timeoutHow long a queued session waits before failing (default 300s)
--session-retry-intervalPolling interval for matching capabilities (default 5s)
SE_NODE_MAX_SESSIONSMax concurrent sessions per Node (default 1)
SE_NODE_SESSION_TIMEOUTInactive 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:

  • GraphQL endpoint at /graphql for session inspection
  • Status endpoint at /status for ready-check
  • Prometheus metrics at /wd/hub/status (Grid health, session counts)

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-patternWhy it failsFix
shm_size default (64 MB) on Chrome nodeChrome crashes mid-sessionAlways shm_size: "2g"
SE_NODE_MAX_SESSIONS too highOOM / CPU thrashingConservative: 2 per 2-core Node
Hub-and-node without health checkFailed nodes silently drop sessionsWait for /status ready before tests start
Standalone in productionNo HA; single point of failureHub-and-node minimum for prod
Manually allocating ports for NodesConflictsLet Docker assign + use service DNS
No session-queue timeoutSessions wait forever; CI hangsSet --session-request-timeout bounded
Mixing Selenium versions across Hub + NodesCapability negotiation breaksPin same Grid version across all components

Limitations

  • No real-device support out of box. Mobile testing needs Appium server + emulators / real devices - significant additional infrastructure.
  • Browser-version matrix is self-managed. Cloud grids ship 3000+ combinations; self-hosted ≤ what your team builds.
  • HA + scaling is your problem. Cloud grids handle it; on self-hosted you do.
  • macOS / iOS Safari requires actual macOS hosts. Linux can run Chrome / Firefox / Edge but not Safari.

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:4443

CI 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 down

Related 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.