How to Integrate Bitbucket Pipelines with Selenium Grid
Run Selenium tests from Bitbucket Pipelines against a reachable Grid, publish test reports, and troubleshoot networking, capacity, and reliability issues.
To integrate Bitbucket Pipelines with Selenium Grid, define a test step in bitbucket-pipelines.yml at the repository root, make a Selenium Grid endpoint reachable from that step, and configure your tests to use Remote WebDriver with that endpoint and browser options. Configure your test framework separately to write JUnit XML or another supported report format; Selenium automates browsers but does not produce test reports.
The example below assumes a Grid is already available at SELENIUM_REMOTE_URL. It does not start a Grid service. If you provision Grid for each build, also configure service networking, startup readiness, and teardown for the Bitbucket runner and runtime you use.
1. Choose where Selenium Grid runs
Pick the execution topology before writing the pipeline. The pipeline container must be able to reach the Grid endpoint over the network.
| Topology | When it fits | Address to use |
|---|---|---|
| Existing standalone Grid | A small suite, a persistent test environment, or a first integration. | The standalone server URL reachable from the build step. |
| Hub and Nodes | You want to route sessions to separately managed browser nodes. | The Hub URL reachable from the build step. |
| Distributed Grid | You operate Grid as separate components and need its distributed topology. | The Router URL reachable from the build step. |
| Grid provisioned for a pipeline | You want build-scoped infrastructure and teardown. | The service hostname and port that the chosen runner/runtime exposes to the step. |
| Managed browser testing service | You need hosted browser and platform coverage without operating Grid yourself. | The provider’s documented endpoint and credentials. |
Selenium documents http://localhost:4444 as the default Grid address. That is only correct when the Selenium client and Grid share the same network namespace. In a Bitbucket build container, localhost refers to that container; it does not automatically mean the runner host or a different service container.
Bitbucket supports custom build images and documents Docker as a step service for steps that need Docker. Runner and cloud-runtime restrictions can differ, so confirm the current runtime documentation before depending on specific Docker flags or service discovery behavior. The official material reviewed here does not provide a universal Bitbucket-specific Grid service-container recipe.
2. Add the Bitbucket pipeline step
Create bitbucket-pipelines.yml in the repository root. This Maven example runs tests and retains the Surefire report files as build artifacts. Replace the image, commands, artifact path, and endpoint convention to match your project.
image: maven:3.9-eclipse-temurin-17
pipelines:
default:
- step:
name: Selenium integration tests
script:
- mvn test
artifacts:
- target/surefire-reports/**
The Grid endpoint must be made available to test code as an environment variable. Set SELENIUM_REMOTE_URL in the step or in the appropriate secured repository, workspace, or deployment variable configuration. Use a secret variable for credentials if the Grid URL contains credentials; do not commit secrets in YAML.
Pin image tags and dependency versions to make runs more repeatable. Choose an image that already includes the language runtime and tools your build needs. If the step must start a Grid with Docker, enable Docker according to the relevant Bitbucket runner documentation and verify that the selected environment allows the required operations.
3. Configure Java tests to use Remote WebDriver
Add Selenium and a test framework such as JUnit to the project’s Maven dependencies. The code below shows the client setup and a minimal JUnit test. It reads the Grid URL from the environment and always quits the browser session.
import java.net.URL;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.RemoteWebDriver;
class RemoteGridTest {
private WebDriver driver;
@BeforeEach
void createDriver() throws Exception {
String remoteUrl = System.getenv("SELENIUM_REMOTE_URL");
if (remoteUrl == null || remoteUrl.isBlank()) {
throw new IllegalStateException("Set SELENIUM_REMOTE_URL to a reachable Grid URL");
}
ChromeOptions options = new ChromeOptions();
driver = new RemoteWebDriver(new URL(remoteUrl), options);
}
@Test
void pageHasExpectedTitle() {
driver.get("https://example.com");
org.junit.jupiter.api.Assertions.assertTrue(driver.getTitle().contains("Example"));
}
@AfterEach
void closeDriver() {
if (driver != null) {
driver.quit();
}
}
}
Use browser options or capabilities for the browser and settings the Grid supports. The Grid must have a matching browser slot available. The exact option set depends on the browser, Selenium binding version, and Grid configuration.
Maven Surefire is commonly used to run JUnit tests and write XML reports beneath target/surefire-reports. Configure the test framework and build plugin for the project’s chosen versions; the pipeline artifact path must match the actual report location.
4. JavaScript client configuration
The Selenium JavaScript API supports configuring the remote server with usingServer. Install Selenium WebDriver and a test runner such as Mocha in the project, then pass the endpoint via environment configuration.
const { Builder, Browser } = require('selenium-webdriver');
async function main() {
const remoteUrl = process.env.SELENIUM_REMOTE_URL;
if (!remoteUrl) throw new Error('Set SELENIUM_REMOTE_URL to a reachable Grid URL');
const driver = await new Builder()
.forBrowser(Browser.CHROME)
.usingServer(remoteUrl)
.build();
try {
await driver.get('https://example.com');
const title = await driver.getTitle();
if (!title.includes('Example')) throw new Error(`Unexpected title: ${title}`);
} finally {
await driver.quit();
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Configure Mocha or your selected framework to emit JUnit-compatible XML if you want Bitbucket to ingest and display results. The Selenium package provides browser automation; the reporter comes from the test framework or a reporting integration.
5. Publish test results
Make report production an explicit part of the integration. Bitbucket can ingest supported test report files and display results. Selenium does not generate those files by itself.
- Choose the test framework and its XML reporter, such as JUnit, TestNG, pytest, or Mocha with a compatible reporter.
- Run tests with the reporting configuration enabled, including when tests fail where the framework supports it.
- Configure Bitbucket’s report ingestion and the step artifact paths for the report format and location you selected.
- Check a failed build to confirm the report is visible and retained for the period your workflow needs.
Use the framework-specific Bitbucket reporting guidance for exact paths and configuration. Do not assume an artifact glob alone configures report ingestion.
6. Make the Grid endpoint reachable and safe
- External Grid: Use a DNS name and port reachable from the runner. Check firewall rules, routing, TLS, and any required authentication.
- Separate service container: Use the service hostname and port provided by the runner, not
localhostby default. Wait for Grid readiness before running tests. - Build-scoped Grid: Start it before the tests, poll a health or readiness endpoint using the mechanism supported by your Grid deployment, and ensure it is stopped even if tests fail.
- Security: Restrict access to trusted clients. Selenium warns that Grid must be protected from external access; an exposed Grid can put infrastructure, internal applications, and files at risk, and may allow binary execution.
- Credentials: Store secrets as secured variables and rotate credentials according to your environment’s practices. Avoid printing them in logs.
Do not expose a Grid endpoint broadly just to make CI connectivity easy. Place it on a restricted network path and permit access only from the required runner or clients.
7. Capacity, parallelism, and reliability
Grid capacity depends on browser types, node resources, test behavior, and the parallel session count. There is no universal Grid size. Start with a conservative level of parallelism, observe queued sessions, session creation failures, test duration, and resource pressure, then adjust in the target environment.
- Keep session cleanup in a
finallyor test-framework teardown hook so failed assertions do not leak browser slots. - Set framework timeouts for remote operations and waits appropriate to the application; prefer condition-based waits over arbitrary fixed delays.
- Separate browser startup failures from application assertion failures in logs and reports.
- Retry only failures known to be transient. Broad automatic retries can hide real defects and increase Grid load.
- For build-scoped infrastructure, make startup readiness explicit and preserve useful Grid logs when the step fails.
- Run the same suite at the same concurrency against the actual CI network and Grid configuration before relying on local results.
Measure pipeline time and infrastructure usage before increasing concurrency. More parallel sessions can reduce wall-clock time when capacity is available, but may also increase queueing and instability when the Grid is saturated.
8. Cost and operational tradeoffs
With a self-managed Grid, account for the machines or containers that host browser nodes, the time spent maintaining browser and driver compatibility, and the effort to secure and monitor the service. Build-scoped Grid can use resources only during a job, but adds startup and teardown work and depends on runner capabilities.
A managed browser-testing service moves Grid operation to a provider and may offer browser coverage suited to a team. Compare its supported browsers, session limits, security model, network access, and pricing against the cost of operating your own Grid. Atlassian’s third-party integration guide describes BrowserStack as supporting Selenium testing; the research available for this article does not establish any referral or affiliate terms.
9. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Connection refused or timeout connecting to Grid | Wrong host or port, Grid is down, network route/firewall blocks access, or service has not started. | From the build step’s network context, verify the configured hostname and port, confirm Grid readiness, and check runner networking and firewall rules. |
localhost:4444 works locally but fails in Pipelines |
Localhost points to the build container, not the host or a separate Grid container. | Use the reachable Grid hostname or address supplied by the actual runner topology. |
| Session not created or no matching capability | The Grid has no free slot for the requested browser, or requested capabilities do not match configured nodes. | Inspect available node/browser slots and align requested browser options with Grid configuration; reduce concurrency if slots are saturated. |
| Tests pass locally but fail only in CI | Different browser version, missing environment configuration, network restrictions, timing differences, or resource contention. | Pin dependencies and build image, log non-secret runtime details, verify the endpoint from the step, and use condition-based waits. |
| Pipeline is green but test results are absent | The framework did not emit supported XML, report path is wrong, or report ingestion is not configured. | Inspect generated files, configure the framework reporter, and follow Bitbucket’s report-ingestion setup for that format. |
| Browser slots remain occupied after failures | Tests do not call quit() when assertions or setup fail. |
Use teardown or finally cleanup and check Grid session state after a failed run. |
| Grid service starts too late or tests race startup | The pipeline starts tests before Grid is ready. | Add an explicit readiness wait with a bounded timeout and emit service logs when readiness is not reached. |
| Docker-based Grid step fails before tests run | The selected runner/runtime does not allow the Docker operation or flags used. | Check current Bitbucket documentation for the selected runner and Docker service configuration; choose a compatible topology. |
10. Do-it-yourself screenshot alternative
If a CI workflow needs screenshots of pages rather than interactive browser tests, a screenshot API can remove browser setup from that part of the job. ScreenshotNeo is a website screenshot API and MCP server for developers. Its one-call endpoint returns an image or PDF; it is not a replacement for Selenium tests that need to interact with a live browser session.
Or skip the browser setup
For an API key and options, see the ScreenshotNeo API documentation. This cURL request saves a WebP capture:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Equivalent Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
Equivalent Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
- Cookie banners, popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers say the page verdict and billing status.
- An MCP server lets AI agents, including Claude and Cursor, take screenshots.
- 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000. Every feature is on every plan.
See ScreenshotNeo for the product and sign up free to get 1,000 screenshots a month without a card.
Frequently asked questions
Does Selenium Grid run inside Bitbucket Pipelines automatically?
No. The pipeline step runs the commands you configure. Provide a Grid that is reachable from that step, whether it is already hosted, provisioned for the job, or provided by a service.
Does Selenium create JUnit reports?
No. Configure your test framework or build tooling to emit the report format Bitbucket supports.
Can I use ScreenshotNeo instead of Selenium Grid?
Use ScreenshotNeo for page screenshots or PDFs through its API. Use Selenium Grid when tests need browser interactions, session control, or test-framework assertions against an application.
How many Grid nodes should I configure?
There is no fixed number that fits every suite. Base capacity on the browsers, resource limits, session concurrency, and observed behavior in your target environment.


