Q34 of 37 · Selenium
How would you set up Selenium Grid 4 in Docker for CI usage?
Short answer
Short answer: Docker Compose with a hub container and chrome/firefox node containers, scaled with `--scale chrome=N`. Tests connect via `RemoteWebDriver` to `http://localhost:4444`. For CI, start before tests, tear down after, and pin image versions to match your test code's Selenium version.
Detail
The minimal viable docker-compose.yml:
version: '3.9'
services:
selenium-hub:
image: selenium/hub:4.20
ports:
- '4442:4442'
- '4443:4443'
- '4444:4444'
chrome:
image: selenium/node-chrome:4.20
shm_size: '2g'
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=3
firefox:
image: selenium/node-firefox:4.20
shm_size: '2g'
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
Critical details that bite you in CI:
shm_size: 2g— without this, Chrome crashes on heavy pages. The default Docker shm is 64MB; far too small.Pin versions —
selenium/hub:4.20not:latest. Match your client library's Selenium version (selenium-java:4.20) to the Grid version. Mismatches cause obscure session-creation errors.Test code points at the hub:
WebDriver driver = new RemoteWebDriver(
new URL("http://localhost:4444"), new ChromeOptions()
);
File uploads:
((RemoteWebDriver) driver).setFileDetector(new LocalFileDetector())— without it, Selenium tries to read the file from the node's filesystem, not yours.Scaling:
docker compose up -d --scale chrome=4 --scale firefox=2
Each replica gets SE_NODE_MAX_SESSIONS slots, so 4 chrome × 3 sessions = 12 concurrent Chromes.
CI integration (GitHub Actions example):
- name: Start Grid
run: docker compose up -d
- name: Wait for hub
run: |
for i in {1..30}; do
curl -sf http://localhost:4444/status && break
sleep 1
done
- name: Tests
run: mvn test -DGRID_URL=http://localhost:4444
- name: Tear down
if: always()
run: docker compose down
Alternatives I'd consider before standing this up:
- Sauce Labs / BrowserStack / LambdaTest — managed Grid. If your CI bill is small, the build-vs-buy maths often favours buy.
- Selenium-on-K8s (KEDA + Grid) — for very large suites with elastic demand.
For most teams, the Docker Compose setup above runs forever — it's a 50-line yaml that handles 90% of needs.
// EXAMPLE
docker-compose.yml
version: '3.9'
services:
selenium-hub:
image: selenium/hub:4.20
ports: ['4444:4444', '4442:4442', '4443:4443']
environment:
- SE_SESSION_REQUEST_TIMEOUT=300
chrome:
image: selenium/node-chrome:4.20
shm_size: '2g'
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=3