Parallel BrowserStack Local Tunnels: When to Share an Identifier and When to Fork the Binary
Running Selenium or Playwright tests against localhost from BrowserStack cloud devices requires a Local tunnel. In parallel CI, the choice between a shared tunnel identifier and per-worker binaries changes both resource usage and failure isolation.
04 Dec 2025, 03:00 UTC

The CI job that can't reach localhost
Your pipeline is green locally. Tests hit localhost:3000, assertions pass, and the job finishes in seconds. You push the same suite to BrowserStack Automate, and suddenly every request times out. The cloud browser has no route to your internal network.
BrowserStack Local solves this by running a binary on your CI machine that opens a secure tunnel to the BrowserStack cloud. Once the tunnel is up, an Automate session can resolve localhost:3000 or an internal staging hostname as if it were on your network. The capability is simple: set browserstack.local: true in your WebDriver or Playwright options.
The hard part is not the capability. It is deciding how many tunnels to run when your CI fans out into parallel workers.
Shared tunnel or per-worker binary?
BrowserStack Local supports two topologies for parallel test execution.
With a shared tunnel, every worker starts tests that reference the same browserstack.localIdentifier. The first worker launches the binary with --local-identifier my-ci-run; subsequent workers simply pass the matching identifier in their capabilities. This reduces memory and process overhead because only one tunnel process exists.
With per-worker tunnels, each parallel job starts its own Local binary, ideally with a unique identifier. This costs more resources, but it decouples failure domains: if one worker's tunnel drops, the other workers keep running.
A third option—no identifier at all—is effectively a shared tunnel with an anonymous name. It works for small suites, but it makes debugging harder because you cannot easily map a session back to a specific CI job.
Worked example: isolating an Automate run
Assume you have a CI matrix with four parallel workers, and you want to restrict tunnel traffic to Automate sessions only. You would start the binary before the test phase and pass an identifier.
# Run in your CI job shell, not on the BrowserStack cloud
# Replace ACCESS_KEY and IDENTIFIER with your own values
./BrowserStackLocal --key ACCESS_KEY --local-identifier IDENTIFIER --only-automate &
The --only-automate flag prevents the tunnel from serving Live or App Automate sessions, which reduces the blast radius if the key leaks. In your test capabilities, the matching identifier tells BrowserStack which tunnel to use:
{
"browserstack.local": true,
"browserstack.localIdentifier": "IDENTIFIER"
}
If you run Playwright, the same fields belong in the BrowserStackLauncher or connect options. If you run Selenium, they go into the BrowserstackOptions or desired capabilities map. The exact SDK method varies by client version, so check the binding documentation for your installed package. This guidance assumes a current Local binary and recent client bindings; an outdated WebDriver client may fail to negotiate the tunnel capability.
When the suite finishes, kill the binary. In CI, leaving orphaned tunnel processes running is a common source of resource leaks.
Trade-offs you will feel in the pipeline
A Local tunnel is a network middleman. It adds latency, and it introduces a single point of failure. If the binary crashes or the network drops, Automate sessions targeting localhost will time out. They will not silently fall back to the public internet, which is good for security but bad for flaky pipelines.
Sharing a tunnel saves processes but couples your workers to one uptime window. If the shared binary dies, every parallel session fails at once. Per-worker tunnels isolate that risk, but they multiply the number of long-lived processes your CI runner must manage.
Secrets are the other sharp edge. The access key and the local identifier are credentials. Embedding them in plain-text CI variables or committing them to a repository exposes your internal endpoints to anyone who can use the tunnel. Mask them in CI logs and rotate them if they leak.
Finally, BrowserStack Local is not a production simulator. It does not replicate your CDN, public DNS records, or third-party service behavior. A test that passes through the tunnel may still fail in production because of caching or geo-routing differences.
Check the tunnel, not just the test
Before you trust a green build, verify that the tunnel actually carried the traffic. Start a local HTTP server on the expected port before the tunnel begins, or the Automate session will fail to connect. Then open the Automate dashboard and inspect the session details. You should see a Local status indicator, and the network logs should show requests to your internal host rather than a public staging URL.
Compare the Local run against a public staging deployment. If the public run passes and the Local run fails, the application under test is likely fine and the tunnel or environment is the problem. If both fail, look at the application.
Confirm that video, console logs, and network HAR files are present for the Local session. BrowserStack records these the same way it does for public URLs, so their absence usually means the session never started correctly.
Treat the Local binary as a first-class CI dependency: start it explicitly, scope it with an identifier, restrict it with --only-automate when possible, and tear it down deterministically. The tunnel is not magic; it is infrastructure you own.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.