How to Run Playwright Tests in Parallel with Sharding
Split Playwright Test across CI jobs with --shard, tune workers safely, balance uneven shards, and merge blob reports into one HTML report.
Run the same Playwright Test command in each of several independent CI jobs, passing every job a different 1-based shard index and the same shard total. For four jobs, use npx playwright test --shard=1/4 through --shard=4/4. Use CI matrix or parallel-job support to run them at the same time. To get one report, configure the blob reporter, collect every shard’s blob report, and run npx playwright merge-reports --reporter html ./all-blob-reports.
Sharding spreads work across machines; the workers setting controls concurrency inside each machine. The total work is therefore shaped by both the number of shards and the workers available to each shard. More parallelism can reduce elapsed time when jobs have capacity and tests are independent, but it can also increase contention and expose data collisions. There is no guaranteed linear speedup.
Commands and configuration below use the stable Playwright documentation. Check the version installed in your project when adopting version-sensitive options. See the official sharding guide, parallelism guide, and CLI reference.
1. Decide how much parallelism to use
There are two layers to choose:
- Shards: separate CI jobs or machines, each running part of the suite.
- Workers: Playwright worker processes running tests concurrently inside a shard.
Start with multiple shards and one worker per CI job if stability and reproducibility are the priority. Playwright recommends one worker in CI as a stability-first setting; this is a recommendation, not a requirement. Increase workers only after considering runner CPU and memory, browser resource use, test isolation, and observed stability. A faster configuration on one suite may be slower or less reliable on another.
For example, four shards with one worker each can run on four jobs. Four shards with two workers each may run more tests concurrently, but each machine must support the extra browser processes and the test data must tolerate that concurrency.
2. Configure Playwright Test
Install Playwright Test and the browser engines used by the suite in the usual project setup. The following configuration uses blob reports in CI and a regular HTML report for local runs:
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
reporter: process.env.CI ? 'blob' : 'html',
// Stability-first CI starting point. Adjust after checking runner capacity.
workers: process.env.CI ? 1 : undefined,
});
Run the suite locally before introducing sharding so that test discovery, browser installation, and report generation work. Playwright’s CI guide recommends limiting browser downloads to engines the suite uses where practical.
3. Run one distinct shard per CI job
The shard format is current/total; current starts at 1. For four jobs, each job runs one command:
npx playwright test --shard=1/4
npx playwright test --shard=2/4
npx playwright test --shard=3/4
npx playwright test --shard=4/4
These are alternatives for four separate jobs, not four commands to run sequentially in one job. Every job needs the same code, Playwright version, configuration, test selection, and total shard count. Give each job exactly one distinct index from 1 through the total.
GitHub Actions example
This workflow creates four test jobs, stores each blob report under a unique artifact name, and merges the reports in a dependent job. Adjust the Node version, browser installation, and project scripts for your repository.
name: Playwright tests
on:
push:
pull_request:
jobs:
playwright-tests:
timeout-minutes: 60
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
cache: npm
- run: npm ci
- run: npx playwright install --with-deps chromium
- name: Run shard
run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
- name: Upload blob report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: blob-report-${{ matrix.shardIndex }}
path: blob-report
retention-days: 1
merge-reports:
if: ${{ !cancelled() }}
needs: [playwright-tests]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
cache: npm
- run: npm ci
- name: Download shard reports
uses: actions/download-artifact@v4
with:
path: all-blob-reports
pattern: blob-report-*
merge-multiple: true
- name: Merge into HTML report
run: npx playwright merge-reports --reporter html ./all-blob-reports
- name: Upload merged report
uses: actions/upload-artifact@v4
with:
name: playwright-html-report
path: playwright-report
retention-days: 14
The action versions above are example workflow configuration. Use versions approved for your repository. The merge job needs the project dependencies and the same Playwright package version to run the merge command. The if: ${{ !cancelled() }} conditions allow report collection after failures while avoiding work after cancellation; provider behavior and cancellation policy may differ. If a shard is cancelled before it uploads, its results cannot appear in the combined report.
Map a CI provider’s job index
CI systems differ in how they expose matrix indices and parallel-job counts. Convert the provider’s zero-based index if necessary: Playwright shard indices are one-based. Validate the mapping by logging the computed shard command in each job before relying on it. Playwright’s CI documentation includes provider-specific examples, including GitHub Actions, CircleCI, GitLab CI, and Azure Pipelines.
For systems that expose a one-based node index and total directly, pass those values in the documented current/total order. For zero-based indices, add one to the index while preserving the total. Avoid hard-coding the total in multiple places where it may drift from the matrix size.
4. Improve shard balance
By default, Playwright distributes test files among shards. Tests within an individual file run sequentially by default, so suites with a few large files and many small ones can produce uneven shards. One slow shard determines when the full CI run finishes.
When tests can safely run independently, enable full parallelism to give sharding finer, test-level granularity:
import { defineConfig } from '@playwright/test';
export default defineConfig({
fullyParallel: true,
reporter: process.env.CI ? 'blob' : 'html',
workers: process.env.CI ? 1 : undefined,
});
You can also opt in at project scope or use test.describe.configure({ mode: 'parallel' }) for selected tests. Parallel mode changes execution assumptions: tests in parallel run in separate worker processes; they cannot share process memory, and relevant hooks run for each parallel test. Review setup and teardown behavior before enabling it. Static skips and fixmes are not counted in shard balancing as runnable work.
Diagnose uneven shards
- Compare each shard’s duration and test list from the CI logs or reports.
- Look for a small number of long files or tests that dominate the slowest shard.
- Consider
fullyParallel: trueif those tests are independent and safe to distribute. - Check job startup, browser installation, and environment provisioning time; test balance does not remove this fixed overhead.
- Change shard count and worker count separately where possible, then compare runs under similar CI conditions.
Do not infer that more shards will always make the pipeline faster. Extra jobs can increase queue time, setup work, contention on shared services, and artifact handling. Measure the slowest job’s duration and the end-to-end pipeline time.
5. Isolate test data across workers and shards
Playwright gives each test an isolated browser context, including separate cookies and local storage. That browser isolation does not isolate your API, database, shared account, inbox, or other external state. Two tests can still overwrite the same record or consume the same one-time resource.
Before increasing parallelism:
- Use unique users, records, namespaces, or tenant IDs per test where practical.
- Make setup and cleanup safe to retry; worker processes may be restarted after failures.
- Avoid ordering dependencies between tests and shards.
- Do not have parallel tests mutate shared accounts or fixed records without coordination.
- Keep external services provisioned for the expected request concurrency.
Playwright documents browser-context isolation in its isolation guide and notes that parallel workers are separate processes in its parallelism guide.
6. Merge shard reports into one HTML report
The blob reporter stores test results and attachments so reports from multiple shards can be merged. Ensure every shard uploads its blob output, and download all those files into one directory. Then run:
npx playwright merge-reports --reporter html ./all-blob-reports
The command writes the standard HTML report to playwright-report by default. Open a locally generated report with:
npx playwright show-report playwright-report
In CI, publish or retain the merged playwright-report directory as an artifact so it can be opened after the job ends. Use unique artifact names for each shard so uploads do not overwrite one another. Playwright’s reporter documentation describes the blob reporter; the sharding guide documents merging reports.
Multiple environments are different from shards
Shards are parts of one test run. If you merge reports from separate environments, such as different operating systems or configurations, tag the environment in the Playwright configuration so the combined report can distinguish them. Follow Playwright’s merge guidance for cross-environment reports and test-root configuration.
7. Useful CLI options and configuration
| Setting | Purpose | Practical guidance |
|---|---|---|
--shard=current/total |
Selects one part of the suite; index is 1-based. | Use a unique current index for each job and the same total everywhere. |
--workers=N or workers |
Limits concurrent worker processes within a job. | Start conservatively in CI; tune for runner capacity and test stability. |
fullyParallel: true |
Allows independent tests to be distributed more finely. | Enable only after reviewing shared state, hooks, and data isolation. |
--project |
Selects browser or other configured project(s). | Keep project selection consistent across shards for a given run. |
--grep, file filters |
Restricts tests selected for execution. | All jobs must use compatible filters; otherwise the merged run omits or duplicates intended coverage. |
--retries |
Retries failed tests. | Retries may help classify intermittent failures but add runtime and do not repair shared-state races. |
--max-failures |
Stops a run after a failure limit. | Can save resources, but fewer tests may complete and appear in that shard’s report. |
--reporter |
Chooses reporter output. | Use blob output for shard merging; add other reporters only when needed. |
The current CLI reference lists supported flags. Avoid having different shard jobs run different test filters or projects unless that division is deliberate and your reporting plan accounts for it.
8. Performance, reliability, and cost
- Performance: End-to-end time depends on job scheduling, setup, browser startup, test duration distribution, and available runner capacity. The slowest shard is the critical path. Use observed timings rather than assuming that doubling shards halves runtime.
- Runner resources: Multiple browser workers compete for CPU and memory. Resource pressure can make tests slower or less stable. Tune workers per machine separately from shard count.
- Reliability: Independent tests and unique backend data reduce race conditions. Save blob artifacts even when tests fail, where the CI provider allows it, so successful and failed shard results remain diagnosable.
- CI cost: More concurrent jobs consume more runner capacity, and provider billing depends on that provider’s pricing and execution model. Include install, startup, retries, and report merge jobs when evaluating the tradeoff.
- Artifacts: Blob archives include test details and attachments. Retain them long enough for diagnosis, but account for storage and artifact transfer limits in your CI system.
Or skip the browser setup
If you need a screenshot of a page as part of a debugging or documentation workflow, ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. One GET request returns an image or PDF. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
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 import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Sign up for ScreenshotNeo and get 1,000 free screenshots a month with no card.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| A shard runs the whole suite or the wrong portion. | Shard arguments are missing, duplicated, or mapped incorrectly from a zero-based CI index. | Log the exact command per job. Use --shard=1/4 through --shard=4/4; convert zero-based indices by adding one. |
| Tests are missing from the merged report. | A shard did not upload its blob directory, was cancelled, or ran a different filter/project selection. | Check each job’s artifact and test command. Upload on failure when supported and ensure every intended shard completed. |
| The merge command says no reports were found. | The merge path does not contain blob report files, or the download step placed them in nested folders. | Inspect the downloaded artifact layout and pass the directory containing the report files. Configure the download step to merge artifact contents into the target directory if needed. |
| Artifacts overwrite each other. | CI artifact names are the same for all shards, or files are copied into a shared location with colliding names. | Include the shard index in artifact names. Playwright blob report filenames include the shard number when sharding is used. |
| One shard takes much longer. | File-level distribution is uneven, a few tests are slow, or that job has resource contention. | Compare test durations and runner load. Consider fullyParallel: true when tests are independent, and tune workers and shard count based on measurements. |
| Tests fail only under parallel execution. | Tests share backend records, accounts, files, or other external state, or setup hooks assume serial execution. | Give tests unique data and review hooks, cleanup, and shared services. Reduce workers while isolating the cause. |
| The merge job never starts after a test failure. | The job dependency uses success-only behavior or workflow cancellation prevents it. | Use the CI provider’s documented condition for running after failed dependencies but not after cancellation; verify provider semantics. |
| Browser installation dominates each job. | Every shard installs engines and system dependencies independently. | Install only the browser engines the suite uses, use supported CI caching where appropriate, and include setup time when choosing shard count. |
| Local report works but CI report is empty. | CI is not using the blob reporter or the artifact step is collecting the wrong directory. | Set reporter: process.env.CI ? 'blob' : 'html', check the blob-report directory, and confirm the merge command receives all shard artifacts. |
FAQ
Does every shard need a separate machine?
No. Shards are independently selected portions of the test run; CI commonly assigns each to a separate job or machine so they can execute concurrently.
Can I run one shard locally?
Yes. Run a single selection such as npx playwright test --shard=2/4 to inspect that portion. It runs only that shard, so it does not validate report merging or the full suite.
Do retries change which tests belong to a shard?
Retries rerun failed tests during that shard’s execution. They do not replace the need for distinct shard indices or consistent test selection across jobs.
Can I merge reports from different operating systems?
Playwright supports merging reports from different environments with appropriate configuration and environment tags. Consult the current merge guidance, especially when test root paths differ.


