Resource library

QA How-To

How to Use Playwright sharding across machines (2026)

Use playwright sharding across machines in CI with balanced workers, isolated data, merged blob reports, stable retries, and practical capacity planning.

20 min read | 2,788 words

TL;DR

Run the same Playwright suite on parallel CI jobs with --shard=1/N through --shard=N/N. Keep the jobs identical, use blob reports, merge their artifacts after all shards finish, and monitor the slowest shard rather than assuming equal duration.

Key Takeaways

  • Run every shard with the same code, dependencies, browser build, environment, and total shard count.
  • Use the 1-based --shard=current/total CLI option, with one unique current value per machine.
  • Enable fullyParallel only when individual tests are truly independent and finer balancing is valuable.
  • Publish blob reports from all shards and merge them in a separate job for one coherent result.
  • Keep account allocation, database records, ports, and artifact names isolated by shard or worker.
  • Choose shard count from measured duration and CI capacity, then review the slowest shard over time.

Playwright sharding across machines means running the same suite in several independent Playwright Test processes, with each process selecting a different fraction through the --shard=current/total option. A four-machine run uses 1/4, 2/4, 3/4, and 4/4. When all jobs use the same repository revision and configuration, their combined selection covers the suite.

The CLI switch is the easy part. A dependable distributed pipeline also needs balanced tests, identical environments, isolated accounts, unique artifacts, failure-aware report merging, and a shard count justified by measurements. This guide builds that operating model step by step for 2026 CI systems, using GitHub Actions for the full example and portable commands for other runners.

TL;DR

npx playwright test --shard=1/4
npx playwright test --shard=2/4
npx playwright test --shard=3/4
npx playwright test --shard=4/4

Run those commands concurrently on four equivalent machines, not sequentially on one shell. Configure blob reporting on CI, upload each shard's blob directory, and merge all blobs after the test jobs complete.

Layer Responsibility Failure to avoid
CI matrix Create every unique current/total pair Missing or duplicate shard
Playwright config Define projects, workers, parallel policy, reporter Different selection or output
Test data Isolate users and records Cross-shard interference
Artifact handling Preserve uniquely named blob reports Overwritten shard evidence
Merge job Download all blobs and create one report Fragmented results
Capacity review Measure queue and slowest-shard time More jobs with no faster feedback

1. How playwright sharding across machines works

The shard expression is 1-based. In current/total, current identifies this invocation and total defines how many partitions exist. Every invocation must use the same total. If two jobs both run 2/4, part of the suite runs twice while another part is absent. If a job uses 2/5 by mistake, the partitions no longer describe the same complete set.

Sharding and local parallelism solve related but different problems. Playwright workers are operating-system processes inside one test command. Shards are separate test commands, usually scheduled on separate machines or containers. If four shard jobs each use three workers, the suite can create roughly twelve test workers at once, plus setup and browser overhead.

The selection stays within Playwright Test. You should not maintain four hand-written lists or folders unless there is a separate ownership reason. Automatic sharding lets new tests join the suite without someone updating CI partitions.

Playwright can shard only work that is eligible for parallel execution. By default, distribution has file-level implications. Enabling fullyParallel lets Playwright consider individual tests for finer distribution. That can improve balance, but it can also expose hidden coupling that sequential execution concealed.

Think of sharding as a test scheduling decision, not a guarantee of linear speedup. Every machine still installs dependencies, starts browsers, performs setup, and talks to the same application. The slowest shard determines when the test stage can finish.

2. Establish a clean, repeatable baseline

Before distributing a suite, make one-machine execution deterministic. Run the exact CI command locally or in a single CI job, record its duration, and confirm that retries are not hiding widespread instability. Sharding multiplies environmental load and can make existing collisions harder to diagnose.

A practical baseline config might be:

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: Boolean(process.env.CI),
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 2 : undefined,
  reporter: process.env.CI ? 'blob' : 'html',
  use: {
    baseURL: process.env.BASE_URL ?? 'http://127.0.0.1:3000',
    trace: 'on-first-retry',
  },
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
  ],
});

The values are examples, not universal tuning recommendations. Two workers per shard may be too many for a small runner or too few for a large one. Keep the setting explicit in CI while you measure.

Pin one lockfile, one Playwright package version, and matching browser binaries. Every shard should use the same environment variables and deployment URL. If jobs begin while a preview deployment is still rolling out, different shards can observe different application versions even though their test code matches.

Run the suite with --list and review projects, grep filters, and ignored tests before scaling. A shard should add only the shard filter. Accidental differences in project or tag filters are a more dangerous coverage gap than a slow pipeline.

3. Choose file-level or test-level distribution

The fullyParallel setting changes the unit Playwright can distribute. Without it, complete files are the practical unit. With it, individual tests can be spread more evenly. Neither mode is automatically correct.

Choice Distribution granularity Strength Main risk
fullyParallel: false Test files Preserves file-local ordering assumptions Uneven large files
fullyParallel: true Individual tests Finer balancing across shards Exposes shared-state coupling
test.describe.configure({ mode: 'serial' }) Serial group Keeps dependent sequence together One slow block limits balance

Use fullyParallel when each test creates its own state, can run in any order, and does not rely on beforeAll mutations that other tests consume. If enabling it breaks tests, investigate the dependency rather than immediately adding sleeps. A test that relies on another test's side effects is difficult to retry, shard, and run selectively.

If file-level mode is necessary, keep files reasonably small and cohesive. A single file containing a long end-to-end scenario set can dominate one shard. Split by feature or business capability while preserving meaningful setup boundaries.

Serial mode is appropriate for a truly ordered workflow, but use it sparingly. A serial group acts as a scheduling block and can reduce balancing opportunities. Often the better design is to provision the required state through an API or fixture so each test remains independent.

Read Playwright parallel testing best practices before turning on suite-wide concurrency. Sharding rewards the same isolation disciplines as local worker parallelism.

4. Prove the shard commands before adding a matrix

Test a small total from the command line. The commands can run on separate terminals or CI jobs:

npx playwright test --shard=1/2
npx playwright test --shard=2/2

Use the list reporter temporarily to see what each invocation selects:

npx playwright test --shard=1/2 --reporter=list
npx playwright test --shard=2/2 --reporter=list

Do not compare only the number of tests. A checkout scenario and a static help-page assertion count as one test each but may have very different durations. Compare wall time, setup time, retry count, and the presence of slow groups.

Filters apply before or alongside the shard selection for that invocation. If the pipeline uses --grep @smoke, every shard must receive the same grep. A single job with a different project or grep value no longer participates in the same logical run.

Keep current and total visible in job names and logs. At job startup, print a compact line with commit SHA, deployment URL, shard pair, Playwright version, Node version, and configured workers. That evidence turns an inconsistent-run suspicion into a quick comparison.

Do not use zero as the first current value. The CLI format is 1/total through total/total. Validate matrix inputs at the CI layer when they are constructed dynamically.

5. Configure playwright sharding across machines in GitHub Actions

The matrix below creates four separate jobs. Each receives the same checkout and setup, then passes its unique index to Playwright:

name: Playwright sharded tests

on:
  pull_request:
  push:
    branches: [main]

jobs:
  test:
    name: shard ${{ matrix.shardIndex }} of ${{ matrix.shardTotal }}
    runs-on: ubuntu-latest
    timeout-minutes: 45
    strategy:
      fail-fast: false
      matrix:
        shardIndex: [1, 2, 3, 4]
        shardTotal: [4]
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v5
        with:
          node-version: lts/*
          cache: npm
      - run: npm ci
      - run: npx playwright install --with-deps
      - name: Run shard
        run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
        env:
          CI: "true"
          BASE_URL: ${{ vars.TEST_BASE_URL }}
      - name: Upload blob report
        if: ${{ !cancelled() }}
        uses: actions/upload-artifact@v4
        with:
          name: blob-report-${{ matrix.shardIndex }}
          path: blob-report
          retention-days: 1

fail-fast: false lets the remaining shards complete after one fails, which usually yields better diagnostic coverage. The artifact step uses a unique name per shard so parallel uploads cannot overwrite each other. The not-cancelled condition keeps evidence after assertion failures while respecting a cancelled workflow.

Use a fixed Node version instead of lts/* if your organization's release controls require exact reproducibility. Cache package downloads if useful, but do not cache authenticated state or mutable test output indiscriminately.

Other CI products use different matrix syntax, but the invariant is portable: N concurrent jobs, the same total N, unique current values 1 through N, and the same test command apart from current.

6. Merge blob reports into one result

The HTML reporter from one shard knows only that shard. Playwright's blob reporter stores test results and attachments in a mergeable format. Configure blob output on CI, upload every shard's blob-report directory, and add a downstream merge job.

  merge:
    name: merge Playwright reports
    if: ${{ !cancelled() }}
    needs: [test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v5
        with:
          node-version: lts/*
      - run: npm ci
      - uses: actions/download-artifact@v5
        with:
          path: all-blob-reports
          pattern: blob-report-*
          merge-multiple: true
      - name: Build combined HTML report
        run: npx playwright merge-reports --reporter=html ./all-blob-reports
      - uses: actions/upload-artifact@v4
        with:
          name: playwright-html-report
          path: playwright-report
          retention-days: 14

The merge job checks out the same revision and installs the same Playwright package so report interpretation matches the shard jobs. It downloads all blob files into one directory. Blob filenames include differentiating information, which prevents normal shard outputs from clashing.

If your organization needs multiple report formats, pass comma-separated reporters or use a merge config with reporter settings. Keep machine-readable JUnit or JSON for CI analytics and HTML for investigation. The report merge does not change test outcomes, it consolidates them.

For deeper reporter configuration, see Playwright HTML reporter guide.

7. Isolate authentication, data, and external resources

Sharding raises the number of simultaneous actors. A shared test user that was stable with one worker may now receive conflicting profile updates, carts, sessions, or permission changes. Browser contexts isolate client storage, but they do not isolate server-side state.

Allocate accounts by worker or shard. testInfo.parallelIndex is designed to identify parallel workers across the run and is useful for choosing a unique account. If an external account pool is used, acquire and release accounts atomically. A simple array indexed without coordination can collide when pipelines overlap.

Generated records should include a run identifier and worker identifier:

import { test, expect } from '@playwright/test';

test('creates an isolated project', async ({ page }, testInfo) => {
  const run = process.env.CI_RUN_ID ?? 'local';
  const projectName = [
    'e2e',
    run,
    testInfo.parallelIndex,
    testInfo.retry,
  ].join('-');

  await page.goto('/projects/new');
  await page.getByLabel('Project name').fill(projectName);
  await page.getByRole('button', { name: 'Create project' }).click();
  await expect(page.getByRole('heading', { name: projectName }))
    .toBeVisible();
});

Isolate local ports, temporary directories, email inboxes, object keys, and queue consumers too. Rate limits may be shared even when data is not. Coordinate expected test concurrency with dependent service owners, and distinguish a rate-limit response from an application regression in reporting.

The Playwright storageState reuse guide explains client authentication reuse, but remember that reused authentication is safe only when concurrent tests can share the same account without server-side interference.

8. Tune workers and shards as one capacity model

Total concurrency is not just the shard count. Four shards with four workers can create sixteen active browser workers. Each may open more than one context or make concurrent API calls. Increasing both controls at once makes it impossible to know which change helped or harmed.

Start from measured single-job behavior. Record:

  • CI queue delay before a machine starts.
  • Dependency and browser installation time.
  • Setup project duration.
  • Test execution duration per shard.
  • Slowest-shard duration.
  • Retry count and infrastructure error rate.
  • Merge and artifact time.

The outcome that matters is feedback time from commit to usable result. Eight shards may execute tests faster but finish later if jobs wait in a constrained runner queue. A test environment may also saturate, making each shard slower and increasing false failures.

Change one dimension at a time. Hold workers constant while trying two, four, or six shards. Then choose the shard count and adjust workers within each machine's CPU and memory budget. Playwright's default worker choice may be suitable locally but too aggressive for shared CI runners, so an explicit CI value makes experiments reproducible.

Track the maximum shard duration, not the average. If three jobs finish in six minutes and one takes sixteen, the stage takes sixteen. The slowest partition is where file structure, serial work, or an unstable dependency needs attention.

9. Handle failures, retries, cancellation, and artifacts

A matrix is one logical quality gate. Configure the workflow so any failed shard makes the overall test job fail. At the same time, preserve results from every shard and allow merging after assertion failures. CI conditions vary, so verify these semantics with an intentionally failing test before trusting the pipeline.

Retries happen inside the shard that selected the test. The retry gets a fresh Playwright worker after a failure, but the server data from the first attempt may remain. Use testInfo.retry in names when the product requires a fresh resource, or clean up robustly before retrying.

Keep artifact paths unique. Blob report handling is designed for merging, but screenshots, videos, custom logs, and downloaded files can still collide if every job uploads to a shared bucket key. Prefix external artifact keys with run and shard identifiers.

A cancelled workflow is different from a failed test. Decide whether partial reports are useful and apply conditions accordingly. A merge of incomplete blobs should be labeled partial rather than presented as full coverage.

Trace collection on first retry gives rich evidence without tracing every successful test. Combine it with request logs and application correlation IDs. Avoid uploading secrets in traces, storage state, environment dumps, or request headers. Review retention and access controls for test artifacts.

For a methodical trace workflow, use debugging Playwright tests with Trace Viewer.

10. Evolve the pipeline with evidence

Distributed suites change continuously. New serial tests, a large spec file, another browser project, or slower environment setup can invalidate an old shard decision. Add a lightweight monthly or release-based review of shard duration percentiles and the slowest files.

Do not promise a fixed speed multiplier. Parallel execution has startup, queue, coordination, and contention costs. Report observed critical-path improvement for your own suite and environment. If the gain plateaus, improve test design or infrastructure instead of adding jobs reflexively.

Use tags or separate projects for business priorities, but do not disguise permanently slow feedback by excluding valuable tests from pull requests without a risk decision. A small smoke gate can run first, followed by the complete sharded suite, with both results visible.

When a suite grows beyond predictable static sharding, historical timing data can inform custom test lists or CI orchestration. Keep the selection auditable and ensure the union still covers all intended tests. Playwright's native --shard option remains the simplest default because it has fewer moving parts.

Document the run contract beside the workflow: shard total, worker policy, test-data allocation, setup ownership, artifact flow, and escalation for environment saturation. Future maintainers should be able to distinguish a partitioning issue from a product failure without reverse-engineering CI YAML.

Interview Questions and Answers

Q: Explain Playwright sharding across machines in one minute.

Each machine starts the same Playwright Test suite with a unique --shard=current/total value. The jobs run concurrently and together select the complete suite. Each shard can also use local workers, so I control both levels of concurrency and merge blob reports after completion.

Q: What is the difference between sharding and workers?

A worker is a process within one Playwright invocation and normally one machine. A shard is a separate invocation and can live on another machine. Four shards with two workers each can produce about eight active test workers, subject to setup and project behavior.

Q: Why can fullyParallel improve shard balance?

It allows distribution at individual-test granularity rather than being constrained mainly by files. That gives the scheduler more small units to spread. I enable it only after confirming tests are independent and safe in arbitrary order.

Q: How do you prevent a missing shard?

I generate current values from a reviewed matrix, keep one fixed total, display the pair in each job name, and verify matrix expansion. The workflow treats all matrix jobs as required, and the merge stage can compare expected artifact count with received artifacts.

Q: Why use blob reports?

Blob reports retain Playwright result data and attachments in a format designed for merging. HTML generated separately in every job remains fragmented. A downstream merge-reports command can turn all blobs into one HTML or machine-readable report.

Q: How do you investigate one shard that is consistently slow?

I compare test and setup durations, retries, serial groups, file sizes, and external calls on that shard. If file-level distribution is active, I split oversized files or safely enable fullyParallel. I also check whether the shard lands on a different runner class or deployment instance.

Q: How do you choose the number of shards?

I use an experiment, not a rule of thumb. I measure end-to-end feedback time, including queue, setup, test, and merge phases, while monitoring environment errors. The chosen count is where more shards stop improving the critical path enough to justify their cost and load.

Common Mistakes

  • Starting shard numbering at zero. Playwright expects current values from 1 through total.
  • Giving jobs different totals, grep filters, projects, commits, browser builds, or target URLs.
  • Multiplying shards and workers without calculating total concurrency.
  • Turning on fullyParallel while tests still share mutable state or depend on execution order.
  • Sharing one account, cart, inbox, record name, port, or artifact key across all workers.
  • Producing separate HTML reports and never creating a complete merged view.
  • Cancelling remaining matrix jobs on the first failure when full diagnostics are required.
  • Merging only on success, which discards the report when it is most valuable.
  • Judging balance by test count instead of observed duration.
  • Adding shards after runner queue or test environment saturation has become the bottleneck.

Conclusion

Playwright sharding across machines is reliable when every job is one consistent member of the same run. Use unique 1/N through N/N arguments, keep code and configuration identical, isolate shared resources, emit blob reports, and merge them after all shards finish. Treat workers and shards as a single concurrency budget.

Begin with two shards and an explicit worker count, capture the slowest-shard and total feedback durations, then compare four shards under the same conditions. Keep the configuration that improves the complete feedback loop without increasing data collisions or environment instability.

Interview Questions and Answers

What is Playwright sharding?

Sharding selects a fraction of a Playwright suite for one test invocation. Multiple invocations use distinct --shard=current/total values and run concurrently, often on separate CI machines. Together, the shards cover the suite.

How is sharding different from workers?

Workers are parallel processes inside one Playwright invocation on one machine. Shards are separate invocations that can run on different machines. A shard can still use multiple workers, so total concurrency is roughly shards multiplied by workers.

What does fullyParallel change for sharding?

With fullyParallel, Playwright can balance individual tests across shards. Without it, distribution is primarily by test file, which can leave uneven durations when files differ greatly. The option is appropriate only when tests do not depend on order or shared mutable state.

How do you merge reports from several shards?

Each shard emits a blob report. CI stores those blob files as uniquely named artifacts, and a downstream job downloads them into one directory. The merge-reports CLI converts the collection into HTML, JUnit, or another configured output.

What can make distributed Playwright tests flaky?

Common causes are shared users, colliding records, environment rate limits, nonidentical browser versions, and jobs pointing to different deployments. I isolate data and accounts, pin the environment, and monitor infrastructure errors separately from product assertions.

How would you select an optimal shard count?

I measure queue delay, setup time, execution time, and the slowest shard. I increase shards until the reduction in critical-path duration is smaller than the added queue, startup, and contention cost. I reevaluate when suite composition or CI capacity changes.

What happens if one shard fails?

The overall test stage must fail even if the other shards pass. I disable matrix fail-fast when I want complete diagnostic coverage, upload artifacts with an always or not-cancelled condition, and run report merging even when a shard reports test failures.

Frequently Asked Questions

How do I run Playwright shards on different machines?

Start one identical CI job per shard and pass --shard=current/total to Playwright Test. Current is 1-based, every job must use the same total, and each current value should appear exactly once.

Does Playwright sharding require fullyParallel?

No. Without fullyParallel, Playwright shards at file granularity. With fullyParallel enabled, it can distribute individual tests, which often improves balance but requires stronger test isolation.

How do I combine Playwright reports from shards?

Configure the blob reporter on shard jobs, upload each blob artifact, download all blobs into one directory in a merge job, and run npx playwright merge-reports --reporter=html on that directory.

Why are my Playwright shards uneven?

Large test files, serial groups, project combinations, slow setup, and variable external services can create imbalance. Enable fullyParallel when safe, split oversized files, and compare observed shard duration rather than only test counts.

Can retries move a failed test to another shard?

A retry belongs to the job that selected the test, so it runs within that shard. Make sure retry artifacts and test data remain unique, and do not rely on another shard to repair shared state.

How many Playwright shards should I use?

There is no universal number. Start with available CI capacity, measure queue time and the slowest shard, then increase only while end-to-end feedback improves and environment pressure stays acceptable.

Should setup run once before all Playwright shards?

A setup project normally runs as part of each shard job unless you design a separate provisioning stage. Per-shard setup is simpler and isolated; shared setup can be faster but needs secure artifact transfer, expiry handling, and concurrency-safe data.

Related Guides