Back to articles
Technology Insight

Scaling Automation: Building a Headless Chrome Cluster with Selenoid on VPS

May 30, 2026

Introduction: The Challenge of Enterprise Automation Scaling

In modern agile software development, continuous integration and continuous deployment (CI/CD) pipelines rely heavily on automated end-to-end (E2E) testing. As application complexity grows, the volume of required test suites scales exponentially. Running hundreds of Selenium or Playwright tests sequentially creates a massive bottleneck, delaying deployments and feedback loops.

Traditionally, teams turned to managed cloud grids like Sauce Labs or BrowserStack, or managed internal Selenium Grids. However, these solutions often introduce prohibitive costs or significant infrastructure overhead. A managed Selenium Grid is notoriously fragile, frequently suffering from memory leaks, zombie browser processes, and inconsistent state between test runs.

To overcome these challenges, enterprise engineering teams are increasingly adopting Selenoid—a lightweight, efficient implementation of the Selenium hub utilizing Docker containers to launch browsers. This guide provides a comprehensive, step-by-step technical blueprint for configuring a Headless Chrome Cluster with Selenoid on a Virtual Private Server (VPS) to power large-scale, enterprise-ready automation testing.

---

Why Selenoid and Headless Chrome on a VPS?

Before diving into the implementation, it is vital to understand why the combination of Selenoid, Headless Chrome, and a VPS offers a superior architecture for automation engineering.

  • Isolated Environments: Selenoid spins up a fresh Docker container for every single test session and terminates it immediately afterward. This guarantees total isolation, preventing data contamination, cookies, or cached states from affecting subsequent tests.
  • Resource Efficiency: Traditional browsers require a graphical user interface (GUI) desktop environment (like Xvfb). Operating Chrome in headless mode eliminates the UI rendering overhead, reducing CPU and RAM consumption by up to 60%. This allows you to achieve significantly higher concurrency on identical hardware.
  • Cost Effectiveness: Deploying this architecture on a high-performance VPS (such as DigitalOcean, Linode, or AWS EC2) costs a fraction of the price of proprietary test cloud subscriptions, granting you complete control over your computing resources and data privacy.
  • Rich Tooling and Monitoring: Selenoid provides built-in support for real-time video recording, VNC console access for live debugging, and comprehensive logging out of the box.
---

Prerequisites and Environment Preparation

To follow this guide, you will need a clean VPS running a modern Linux distribution (Ubuntu 22.04 LTS or 24.04 LTS recommended). Ensure your VPS meets the minimum hardware requirements based on your desired concurrency:

Hardware Estimation Rule: As a rule of thumb, allocate approximately 0.5 CPU cores and 500MB to 700MB of RAM per concurrent Headless Chrome instance. For a cluster running 20 parallel browsers, a VPS with 8 vCPUs and 16GB RAM is ideal.

Step 1: System Update and Docker Installation

First, connect to your VPS via SSH and update the package repository to ensure all system dependencies are current:sudo apt update && sudo apt upgrade -y

Next, install Docker and Docker Compose, which are required to orchestrate the Selenoid containers and configuration:sudo apt install apt-transport-https ca-certificates curl software-properties-common -y curl -fsSL [https://download.docker.com/linux/ubuntu/gpg](https://download.docker.com/linux/ubuntu/gpg) | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] [https://download.docker.com/linux/ubuntu](https://download.docker.com/linux/ubuntu) $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update && sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y

Verify that Docker is running correctly by executing:sudo systemctl status docker---

Configuring the Selenoid Cluster

Selenoid relies on a simple JSON configuration file named browsers.json to map requested browser versions to specific Docker images. We will configure it to use optimized, headless-ready Chrome images provided by the Aerokube team.

Step 2: Create the Selenoid Configuration Directory

Establish a dedicated directory structure on your VPS to house the configuration files:

sudo mkdir -p /etc/selenoid

Step 3: Define the browsers.json File

Create and edit the browsers.json file:

sudo nano /etc/selenoid/browsers.json

Paste the following configuration into the file. This defines the Chrome versions available to your automation scripts:{ "chrome": { "default": "120.0", "versions": { "120.0": { "image": "selenoid/vnc:chrome_120.0", "port": "4444", "path": "/" }, "121.0": { "image": "selenoid/vnc:chrome_121.0", "port": "4444", "path": "/" } } } }

Note: Even though we specify the VNC images, we will pass explicit capabilities in our test scripts to force Headless execution, preserving the option to enable VNC debugging when troubleshooting localized environment issues.

Step 4: Pulling the Required Browser Images

To prevent execution delays during your initial test runs, pre-pull the required Docker images onto your VPS:

sudo docker pull selenoid/vnc:chrome_120.0
sudo docker pull selenoid/vnc:chrome_121.0
sudo docker pull aerokube/selenoid:latest-release
---

Orchestrating Selenoid with Docker Compose

Using Docker Compose allows us to manage Selenoid alongside its companion UI (Selenoid UI) effortlessly, streamlining monitoring and cluster management.

Step 5: Writing the docker-compose.yml

Create a docker-compose.yml file in your preferred working directory (e.g., ~/selenoid-cluster/):

mkdir ~/selenoid-cluster && cd ~/selenoid-cluster
nano docker-compose.yml

Populate the file with the following multi-service configuration:version: '3.8' services: selenoid: image: "aerokube/selenoid:latest-release" container_name: selenoid ports: - "4444:4444" volumes: - "/var/run/docker.sock:/var/run/docker.sock" - "/etc/selenoid:/etc/selenoid:ro" - "/root/selenoid/video:/opt/selenoid/video" - "/root/selenoid/logs:/opt/selenoid/logs" environment: - TZ=UTC command: ["-conf", "/etc/selenoid/browsers.json", "-limit", "20", "-video-output-dir", "/opt/selenoid/video", "-log-output-dir", "/opt/selenoid/logs"] restart: always selenoid-ui: image: "aerokube/selenoid-ui:latest-release" container_name: selenoid-ui ports: - "8080:8080" command: ["--selenoid-uri", "http://selenoid:4444"] depends_on: - selenoid restart: always

Crucial parameters configured in this file include:

  1. /var/run/docker.sock mapping: Allows Selenoid to communicate with the host's Docker daemon to spawn sibling browser containers.
  2. -limit 20: Restricts the maximum number of concurrent browser containers allowed to run simultaneously, preventing resource exhaustion on the VPS.
  3. Selenoid UI: Exposes a clean web dashboard on port 8080 to visualize real-time cluster utilization.

Step 6: Launching the Cluster

Initialize and start your cluster services in detached mode:

sudo docker compose up -d

Verify that both containers are active and healthy:

sudo docker compose ps
---

Optimizing for Large-Scale Execution

To run a truly enterprise-scale automation framework processing thousands of test cases, default Linux configurations must be optimized to manage high network throughput and file handle consumption.

Adjusting File Descriptors and Kernel Limits

Each concurrent browser and network connection opens file descriptors. Modify the system configuration to handle heavy loads:

sudo nano /etc/security/limits.conf

Add the following lines at the bottom of the file:* soft nofile 65535 * hard nofile 65535 root soft nofile 65535 root hard nofile 65535

Apply these network optimizations in /etc/sysctl.conf to improve TCP socket reuse rates under load:

sudo nano /etc/sysctl.conf

Append the following kernel parameters:

net.core.somaxconn = 1024
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 1024 65000

Apply the changes immediately by executing: sudo sysctl -p.

---

Connecting Your Test Automation Framework

With your headless cluster fully operational, configure your automation framework to point to the Selenoid hub address: http://:4444/wd/hub.

Below is an enterprise-pattern implementation using Python with Selenium, optimized specifically for Headless Chrome execution within Selenoid:from selenium import webdriver from selenium.webdriver.chrome.options import Options def create_remote_headless_driver(): chrome_options = Options() # Strict Headless Configuration chrome_options.add_argument("--headless=new") chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--disable-dev-shm-usage") chrome_options.add_argument("--disable-gpu") # Selenoid Specific Capabilities selenoid_capabilities = { "browserName": "chrome", "browserVersion": "121.0", "selenoid:options": { "enableVNC": False, # Set to True for live debugging "enableVideo": False, # Set to True to record execution "name": "Regression Test Suite - Account Validation" } } chrome_options.set_capability("cloud:options", selenoid_capabilities) # Point to the VPS Selenoid Hub vps_hub_url = "http://:4444/wd/hub" driver = webdriver.Remote( command_executor=vps_hub_url, options=chrome_options ) return driver # Execution Example if __name__ == "__main__": driver = create_remote_headless_driver() driver.get("[https://www.google.com](https://www.google.com)") print(f"Successfully loaded page title: {driver.title}") driver.quit()---

Securing the Cluster

Exposing ports 4444 and 8080 completely unrestricted to the public internet presents significant security risks. It is imperative to restrict access using a firewall or reverse proxy.

UFW Firewall Configuration

We recommend restricting access to the Selenoid hub exclusively to the IP addresses of your CI/CD runners (e.g., Jenkins, GitHub Actions runners, or GitLab CI nodes):

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh

# Allow a specific CI/CD Server IP to access Selenoid Hub
sudo ufw allow from  to any port 4444

# Enable the Firewall
sudo ufw enable
---

Conclusion and Next Steps

By migrating from traditional execution models to a Headless Chrome Cluster managed via Selenoid on a VPS, you build an enterprise-grade testing infrastructure capable of handling extensive parallel test suites. This architecture drastically reduces execution times from hours to minutes, improves stability, and maintains a predictable, cost-contained infrastructure footprint.

As next steps to advance your grid maturity, consider setting up an Nginx Reverse Proxy with Basic Authentication to protect the Selenoid UI dashboard, and integrating Prometheus and Grafana metrics to monitor your container cluster performance in real-time.

Scaling Automation: Building a Headless Chrome Cluster with Selenoid on VPS | DPTCloud