Setup Selenium Grid 4 with Docker Compose | Easy & Scalable

Why Your CI/CD Pipeline is Failing: Solving Flaky Tests with an Ephemeral Selenium Grid and Docker Compose

Mastering Selenium Grid 4: A Deep Dive into Docker Compose for Scalable Browser Testing

Modern test automation demands speed, scalability, and consistency. For teams leveraging Selenium, setting up a distributed testing environment has been streamlined by the synergy of Selenium Grid 4 and Docker. This guide explores how Docker Compose has become the definitive, officially recommended method for deploying a robust, containerized browser testing farm in minutes, transforming a once-complex task into a simple, declarative process.

The Official Paradigm Shift: Why Docker Compose is the Recommended Path

In the past, configuring a Selenium Grid involved manual downloads, complex command-line arguments, and careful management of Java processes. This approach was often brittle and difficult to reproduce across different environments. With the evolution of containerization, the Selenium project has fully embraced Docker as the premier solution for Grid deployment. The official SeleniumHQ/docker-selenium repository on GitHub is now the canonical source for Grid images and best practices.

The project’s documentation makes its position clear:

“Docker Compose is the simplest way to start a Grid.” – source: SeleniumHQ/docker-selenium

This endorsement is not just about convenience; it represents a fundamental shift towards infrastructure-as-code for test environments. By defining the entire Grid topology in a single, version-controlled docker-compose.yml file, teams gain unprecedented reproducibility, portability, and ease of management. Whether on a developer’s laptop or a CI/CD runner, the Grid behaves identically, eliminating “it works on my machine” issues for automation infrastructure.

Anatomy of a Dockerized Selenium Grid 4

To effectively use Docker Compose, it’s essential to understand the components it orchestrates. Selenium Grid 4 features a more sophisticated, distributed architecture compared to its predecessors. A typical setup orchestrated by Compose includes:

  • Selenium Hub (or Fully Distributed Components): In a classic setup, the Hub acts as the central entry point for all test requests. It receives a new session request, queries the Nodes for a match based on capabilities, and forwards the test commands. In a fully distributed setup, this role is split among a Router, Distributor, and Session Map for enhanced scalability.
  • Browser Nodes: These are the worker containers where the actual browsers run. The official project provides images for Chrome, Firefox, and Edge (e.g., selenium/node-chrome, selenium/node-firefox). Each Node registers itself with the Hub, advertising its available browser types and versions.
  • Event Bus: This is the internal messaging system that allows the Hub and Nodes to communicate. The Hub publishes messages, and the Nodes subscribe to them, enabling seamless discovery and session management.

Docker Compose manages these components as interconnected services, using a private Docker network to facilitate communication. This container-to-container networking is what allows a Node service to find the Hub service using a simple hostname like selenium-hub.

Step-by-Step Guide: Launching Your First Grid with Docker Compose

Let’s translate theory into practice. Launching a cross-browser Selenium Grid is now a straightforward process that can be completed in minutes. As outlined in multiple tutorials, the core workflow involves creating a YAML file and running a single command.

Step 1: Create Your `docker-compose.yml` File

The heart of your setup is the docker-compose.yml file. It declaratively defines every service, its configuration, and its relationship to other services. Here is a practical example for a classic Grid with a Chrome and a Firefox node, incorporating best practices for stability and debugging.

version: "3"
services:
  chrome:
    image: selenium/node-chrome:4.15.0-20231121
    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=5
      - SE_NODE_SESSION_TIMEOUT=300

  firefox:
    image: selenium/node-firefox:4.15.0-20231121
    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

  selenium-hub:
    image: selenium/hub:4.15.0-20231121
    container_name: selenium-hub
    ports:
      - "4442:4442"
      - "4443:4443"
      - "4444:4444"

Let’s break down the key configurations in this file:

  • image: selenium/node-chrome:4.15.0-20231121: We use a specific versioned image tag instead of latest. This is a critical practice for ensuring reproducibility and avoiding unexpected breakages when new image versions are released. You can find available tags on the Selenium Docker Hub page.
  • shm_size: 2gb: As recommended in expert guides like this one from QA Automation Expert, allocating 2 gigabytes of shared memory (/dev/shm) to browser nodes prevents random crashes, a common issue with Chrome and Firefox running in memory-constrained container environments.
  • depends_on: - selenium-hub: This ensures that the Hub container is started before the Node containers attempt to register with it.
  • environment variables:
    • SE_EVENT_BUS_HOST=selenium-hub: This tells the Node container where to find the Event Bus. The hostname selenium-hub is resolved by Docker’s internal DNS to the IP address of the Hub container.
    • SE_EVENT_BUS_PUBLISH_PORT / SUBSCRIBE_PORT: These variables explicitly define the communication ports for the Grid’s messaging system.
    • SE_NODE_MAX_SESSIONS=5: This optional variable configures the Chrome node to handle up to 5 parallel test sessions, increasing throughput.
    • SE_NODE_SESSION_TIMEOUT=300: This sets a 300-second timeout for idle sessions, which can help clean up stale sessions from aborted test runs.
  • ports mapping: The Hub’s ports 4442, 4443, and 4444 are exposed to the host machine. Port 4444 is the primary port for accepting new WebDriver session requests and for accessing the Grid’s UI.

Step 2: Launch the Grid with One Command

With the docker-compose.yml file saved in your directory, starting the entire Grid is as simple as running a single command in your terminal.

docker compose up -d

As noted in guides from Automated Testing with Tuyen and DEV Community, the -d flag is crucial. It runs the containers in “detached” mode, meaning they run in the background and your terminal is freed up. This is essential for integrating the Grid into automated CI/CD pipelines.

Step 3: Verify the Grid is Running

Once the command completes, you can verify that your Grid is operational. Open a web browser on your host machine and navigate to the Grid UI.

“Open any browser and enter the url: localhost:4444/ui. The grid runs on the port 4444 by default, as defined in the docker compose file.” – source: DEV Community

You should see a dashboard displaying the registered Chrome and Firefox nodes, along with their available session capacity. This UI is your central point for monitoring active sessions and the overall health of your Grid.

Step 4: Connect Your Tests

To run your tests, you simply need to configure your Selenium test framework to use a RemoteWebDriver instance pointing to the Hub’s URL. For example, in Java:

// Desired browser
ChromeOptions chromeOptions = new ChromeOptions();

// Point to the Grid Hub
URL gridUrl = new URL("http://localhost:4444");

// Create a RemoteWebDriver instance
WebDriver driver = new RemoteWebDriver(gridUrl, chromeOptions);

// Your test logic here...
driver.get("https://www.google.com");
// ...
driver.quit();

The Grid will receive the request, see that you requested Chrome, and route the session to the available selenium/node-chrome container.

Step 5: Tear Down the Grid

When you are finished with testing, dismantling the entire environment is just as easy as creating it. This single command stops and removes all the containers, networks, and volumes defined in your Compose file, ensuring a clean state.

docker compose down

Advanced Techniques and Real-World Use Cases

The basic setup is powerful, but the true value of the `docker-selenium` project shines in its advanced capabilities and flexibility for real-world scenarios.

Live Debugging and Session Inspection with noVNC

One of the most challenging aspects of automated testing is debugging flaky tests where the cause of failure is not immediately obvious. The `docker-selenium` images come with a built-in noVNC server, allowing you to view and interact with the browser inside the container in real-time. To enable this, you simply map the Node’s internal port 7900 to a unique port on your host machine.

Let’s modify our docker-compose.yml to enable noVNC for the Chrome node:

  chrome:
    image: selenium/node-chrome:4.15.0-20231121
    shm_size: 2gb
    depends_on:
      - selenium-hub
    ports:
      - "7900:7900" # Map the VNC port
    environment:
      # ... same environment variables as before

After running docker compose up -d, you can navigate to http://localhost:7900 in your browser. You will be prompted for a password, which is secret by default. Once connected, you can watch your automated tests execute live inside the container, providing invaluable insight into unexpected application behavior or timing issues.

Ephemeral Grids for CI/CD Pipelines

The one-command lifecycle of Docker Compose makes it a perfect fit for continuous integration environments. A common pattern is to create an “ephemeral” Grid for each pipeline run:

  1. At the start of a CI job: Run docker compose up -d --wait. The --wait flag ensures the command only exits after the containers are healthy.
  2. Run tests: Execute your test suite, pointing it to the Grid at http://localhost:4444 (or the runner’s IP).
  3. At the end of the job: Run docker compose down in a cleanup step. This guarantees that no resources are left behind, and the next job starts with a completely fresh environment.

This approach provides a pristine, isolated testing farm for every build, a best practice for reliable and deterministic test results.

The Dynamic Grid: Scaling On-Demand

For more advanced use cases, the `docker-selenium` project offers a “Dynamic Grid” topology. In this model, the Grid can dynamically start and stop Node containers as needed, optimizing resource usage. This is facilitated by giving the Grid’s Distributor component access to the host’s Docker socket.

Configuration for a dynamic grid is more complex but incredibly powerful. The key is that the Grid’s components can spawn new browser node containers on the fly. As the official documentation notes:

“Containers can be further configured through environment variables… When a child container is created, all environment variables prefixed with SE_ will be forwarded and set in the container.” – source: SeleniumHQ/docker-selenium

This allows you to define a template for your nodes (e.g., via a TOML configuration file mounted into the Grid), and the Grid will launch new instances of that template whenever test demand exceeds the current capacity. You can find ready-made examples for this pattern in the compose-examples directory of the official repository.

Conclusion: The Future of Selenium Testing is Containerized

Selenium Grid 4 with Docker Compose is more than just a convenience; it is the modern standard for building reliable, scalable, and reproducible browser automation infrastructure. By leveraging declarative YAML files, versioned images, and a simple command-line interface, teams can eliminate environmental drift and focus on writing quality tests. The ability to fine-tune resources, debug live with noVNC, and integrate seamlessly into CI/CD pipelines makes this the definitive solution for any serious automation effort.

Ready to build your own testing farm? Explore the official docker-selenium repository to find pre-built Compose files and advanced configuration options. Start small, experiment with the different topologies, and unlock the full potential of distributed testing today. Share this guide with your team to help them get started on their containerization journey.

Leave a Reply

Your email address will not be published. Required fields are marked *