DevOps 3 min read

Integrate Selenium Grid with Azure DevOps

Step-by-step guide to integrating Selenium Grid with Azure DevOps pipelines. Covers Docker-based Selenium Grid setup, self-hosted agents, browser node.

I
InnovateBits
InnovateBits

Selenium Grid lets you run tests across multiple browsers and operating systems in parallel. Integrating it with Azure DevOps gives you scalable, parallel cross-browser test execution in CI without paying for a cloud testing platform.


Selenium Grid architecture in Azure DevOps

Azure Pipeline Agent
       │
       ▼
Docker Compose (on the agent)
  ├── Selenium Hub      (distributes sessions)
  ├── Chrome Node 1     (runs Chrome tests)
  ├── Chrome Node 2     (parallel Chrome)
  └── Firefox Node      (runs Firefox tests)
       │
       ▼
Test code connects to Hub → Hub routes to available Node

Docker Compose for Selenium Grid

YAML
1# docker-compose.selenium-grid.yml 2version: "3" 3services: 4 selenium-hub: 5 image: selenium/hub:4.21.0 6 container_name: selenium-hub 7 ports: 8 - "4444:4444" 9 environment: 10 - SE_NODE_MAX_SESSIONS=4 11 12 chrome-node-1: 13 image: selenium/node-chrome:4.21.0 14 shm_size: '2gb' 15 depends_on: 16 - selenium-hub 17 environment: 18 - SE_EVENT_BUS_HOST=selenium-hub 19 - SE_EVENT_BUS_PUBLISH_PORT=4442 20 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 21 - SE_NODE_MAX_SESSIONS=2 22 - SE_VNC_NO_PASSWORD=1 23 24 chrome-node-2: 25 image: selenium/node-chrome:4.21.0 26 shm_size: '2gb' 27 depends_on: 28 - selenium-hub 29 environment: 30 - SE_EVENT_BUS_HOST=selenium-hub 31 - SE_EVENT_BUS_PUBLISH_PORT=4442 32 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 33 34 firefox-node: 35 image: selenium/node-firefox:4.21.0 36 shm_size: '2gb' 37 depends_on: 38 - selenium-hub 39 environment: 40 - SE_EVENT_BUS_HOST=selenium-hub 41 - SE_EVENT_BUS_PUBLISH_PORT=4442 42 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443

Connecting tests to the Grid

JAVA
1// Java WebDriver connecting to Grid 2ChromeOptions options = new ChromeOptions(); 3options.addArguments("--no-sandbox"); 4 5WebDriver driver = new RemoteWebDriver( 6 new URL("http://localhost:4444/wd/hub"), 7 options 8);
PYTHON
1# Python WebDriver connecting to Grid 2from selenium import webdriver 3from selenium.webdriver.chrome.options import Options 4 5options = Options() 6options.add_argument("--no-sandbox") 7 8driver = webdriver.Remote( 9 command_executor="http://localhost:4444/wd/hub", 10 options=options 11)

Azure Pipelines YAML

YAML
1trigger: 2 branches: 3 include: [main] 4 5pool: 6 name: Self-Hosted-Linux # Self-hosted agent with Docker installed 7 # Microsoft-hosted agents don't persist Docker between steps — use self-hosted 8 9steps: 10 - script: | 11 docker-compose -f docker-compose.selenium-grid.yml up -d 12 echo "Waiting for Grid to be ready..." 13 timeout 60 bash -c 'until curl -s http://localhost:4444/wd/hub/status | grep -q "ready.*true"; do sleep 2; done' 14 displayName: Start Selenium Grid 15 16 - task: JavaToolInstaller@0 17 inputs: 18 versionSpec: '17' 19 jdkArchitectureOption: x64 20 jdkSourceOption: PreInstalled 21 22 - script: | 23 mvn test \ 24 -DGRID_URL=http://localhost:4444/wd/hub \ 25 -DBASE_URL=$(STAGING_URL) \ 26 -Dsurefire.failIfNoSpecifiedTests=false 27 displayName: Run cross-browser tests 28 29 - task: PublishTestResults@2 30 inputs: 31 testResultsFormat: JUnit 32 testResultsFiles: '**/surefire-reports/TEST-*.xml' 33 testRunTitle: Selenium Grid — $(Build.BuildNumber) 34 mergeTestResults: true 35 condition: always() 36 37 - script: docker-compose -f docker-compose.selenium-grid.yml down -v 38 displayName: Stop Selenium Grid 39 condition: always()

Running tests in parallel across browsers

JAVA
1// TestNG parallel execution across browsers 2@DataProvider(name = "browsers", parallel = true) 3public Object[][] browsers() { 4 return new Object[][] { 5 { "chrome" }, 6 { "firefox" }, 7 }; 8} 9 10@Test(dataProvider = "browsers") 11public void checkoutTest(String browserName) throws MalformedURLException { 12 MutableCapabilities caps; 13 if ("chrome".equals(browserName)) { 14 caps = new ChromeOptions(); 15 } else { 16 caps = new FirefoxOptions(); 17 } 18 19 WebDriver driver = new RemoteWebDriver( 20 new URL("http://localhost:4444/wd/hub"), caps 21 ); 22 // Run test... 23 driver.quit(); 24}

Common errors and fixes

Error: Grid hub starts but nodes don't register Fix: Nodes register via the event bus on ports 4442/4443. Ensure these ports are exposed and the SE_EVENT_BUS_HOST env var points to the hub service name (not localhost).

Error: "No such session" errors during parallel execution Fix: Sessions are per-driver instance. Don't share WebDriver instances across threads. Create a new driver per test method.

Error: shm_size has no effect and Chrome crashes Fix: Add --disable-dev-shm-usage to Chrome options. In Docker, /dev/shm is limited to 64MB by default — Chrome needs more for rendering. The option disables shared memory use.

Error: Grid shows nodes as available but tests queue indefinitely Fix: Check SE_NODE_MAX_SESSIONS on each node. If set too low, requests queue. Set to match available CPU cores (2 sessions per CPU core is a good rule).

Tags
#selenium-grid#azure-devops#cross-browser-testing#docker#selenium#parallel-testing

Share this article

Follow for more

Follow me on social media for more developer tips, tricks, and tutorials. Let's connect and build something great together!