Selenium Grid & Parallel Testing
One test, many browsers. Selenium Grid routes WebDriver sessions to remote nodes so suites run in parallel across Chrome, Firefox and Edge. Learn the Router/Node architecture, run Grid with Docker, and drive parallel execution from the test runner.
What is this?
Selenium Grid is a server-side component that distributes WebDriver sessions across multiple machines and browsers. In Grid 4 a Router accepts new-session requests and forwards them to Nodes that run the actual browsers; Grid can also route to external cloud grids. Tests connect through RemoteWebDriver with a Capabilities object, and parallelism is orchestrated by the test runner (TestNG, JUnit or pytest), which opens many sessions concurrently. Grid turns a single-slot laptop into a browser farm that runs a full suite in minutes.
Why do you need it?
- Parallel execution across nodes cuts suite wall-clock time from hours to minutes.
- One grid serves many teams, so tests no longer depend on per-developer browser installs.
- A browser matrix (Chrome, Firefox, Edge, Safari) is exercised in a single run with no test changes.
- Nodes can be added or removed without touching test code — scale by adding containers.
- Running on remote nodes keeps browsers isolated from local state and frees developers' machines.
Mental model
A food-court kitchen with many cooks
Selenium Grid
Orders (sessions) are routed to whichever cook (node) is free and can make the requested dish (browser). More cooks mean more orders in parallel — you never rewrite the menu to add a cook.
How it works
- The Router receives a new-session request with desired capabilities and picks a matching Node.
- The Node spawns a WebDriver session in the requested browser and returns a session id and remote endpoint.
- Tests create RemoteWebDriver pointing at the grid's address (e.g. http://localhost:4444) with capabilities set.
- The test runner launches N concurrent tests, each creating a session that the grid places on a free node.
- max-sessions and per-node browser config control how many parallel browsers a single node can host.
How a test session flows through Selenium Grid 4.
Key commands
docker compose up -dStart the grid router and nodes defined in docker-compose.yml.
Container grid-router Started
docker compose up --scale node-chrome=3 -dScale the Chrome node service to three containers for more parallelism.
docker run -d -p 4444:4444 --name grid selenium/standalone-chrome:4.25.0Run a single all-in-one grid node for quick local experiments.
curl http://localhost:4444/statusCheck grid health and which nodes/capabilities are registered.
{"value":{"nodes":[{"id":"chrome-node","status":"UP","maxSessions":3}]}}mvn test -Dsurefire.suiteXmlFiles=testng-parallel.xmlRun a TestNG suite whose XML declares parallel tests.
docker compose logs node-chromeInspect a node's session lifecycle and driver errors.
Examples
Point a test at the Grid with RemoteWebDriver
import java.net.URL;import java.time.Duration;import org.openqa.selenium.WebDriver;import org.openqa.selenium.chrome.ChromeOptions;import org.openqa.selenium.remote.RemoteWebDriver; ChromeOptions options = new ChromeOptions();options.setCapability("browserVersion", "stable");options.setCapability("platformName", "linux"); WebDriver driver = new RemoteWebDriver( new URL("http://localhost:4444"), options); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));driver.get("https://example.com");System.out.println(driver.getTitle()); driver.quit();Instead of new ChromeDriver(), the test builds ChromeOptions, sets browser capabilities, and creates a RemoteWebDriver against the grid URL. The grid routes the session to a node that can serve a stable Chrome on linux. From there the test is identical to a local one — only the driver construction changes.
In production
- Run the full regression nightly across the browser matrix with zero developer intervention.
- Burst-scale nodes on CI: spin up five nodes for the suite, tear them down afterward.
- Provide an internal browser farm for feature teams instead of local driver installs.
- Route rare-browser tests (Safari via a cloud grid) through the same RemoteWebDriver code path.
Best practices
- Pin browser and driver versions on every node via a fixed Docker image tag.
- Keep max-sessions low enough to avoid starving node memory — one or two per container is typical.
- Run one node per Docker container so a crashed browser cannot take down its neighbors.
- Keep test code browser-agnostic: inject capabilities from config, never hard-code them.
- Parallelize at the test-runner level (TestNG suite, pytest-xdist) instead of hand-rolled threads.
Common mistakes
- Running hundreds of sessions against nodes with too little memory, causing random browser crashes.
- Hard-coding localhost in RemoteWebDriver and breaking the tests in grid or remote runs.
- Ignoring the /status health check and blaming tests for infrastructure failures.
- Using a single static driver with parallelism that expects one shared browser.
Common issues
Unable to find a matching set of capabilitiesNo registered node advertises the requested browser or version.
Check /status, align the requested capabilities with a node, or start a node that serves them.
Max limit of 1 active sessions reachedThe node has no free session slots.
Raise max-sessions, add more nodes, or cap the runner's parallelism.
ERR_CONNECTION_REFUSED connecting to the hubThe grid is not running or the URL/port is wrong.
Verify containers are up and that the RemoteWebDriver URL matches the exposed port.
Debugging tips
- Hit /status to confirm nodes are registered and inspect their browser/version capabilities.
- Watch node containers with docker compose logs for session start/stop and driver errors.
- Run one session serially against the grid before enabling parallelism.
- Capture which node handled each session (session id + node id) to correlate failures.
Interview checkpoint
Hands-on lab
- Run a grid router and Chrome node with Docker Compose
- Verify registration with the /status endpoint
- Point RemoteWebDriver tests at the grid
- Scale nodes and observe sessions running in parallel
Stand Up a Grid with Docker and Run Parallel Tests
40 min · advanced
Quick quiz
Which class does a test use to talk to a grid instead of a local browser?
Grid requires the tests to know which physical node will run them.
/status shows zero nodes and every session fails with 'unable to find matching capabilities'. What is the best fix?
Cheat sheet
docker compose up -dStart grid router + nodesdocker compose up --scale node-chrome=3 -dScale a node servicecurl http://localhost:4444/statusGrid health + capabilitiesdocker compose logs node-chromeNode session logsdocker compose downTear down the gridbrowserNamechrome, firefox, edgebrowserVersionPin a browser versionplatformNamelinux, windows, macmax-sessionsConcurrent browsers per nodese:nodeMaxSessionsGrid 4 per-node session cap