Q35 of 42 · Playwright
How would you set up sharding across multiple CI runners without Playwright Cloud?
Short answer
Short answer: Use the built-in `--shard=N/M` flag. Each CI runner passes a unique shard index and the total. Playwright partitions tests deterministically by file and test count. For duration-balanced shards, post-process JUnit timings and feed them to a custom shard mapping. No cloud service required.
Detail
Playwright has built-in sharding without any external service:
npx playwright test --shard=1/4 # runner 1 of 4
npx playwright test --shard=2/4 # runner 2 of 4
# ... etc
Each runner picks up roughly 1/4 of the tests. Playwright's default partition is by file then test count — usually balanced for similar-cost specs.
GitHub Actions matrix:
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8]
steps:
- run: npx playwright test --shard=${{ matrix.shard }}/8
Eight parallel runners; each tackles 1/8 of the suite.
Combine with projects for cross-browser × shard:
matrix:
browser: [chromium, firefox, webkit]
shard: [1, 2, 3]
steps:
- run: npx playwright test --project=${{ matrix.browser }} --shard=${{ matrix.shard }}/3
For duration-balanced sharding (when default partition produces uneven shards), post-process JUnit timings and assign tests:
- After each successful CI run, parse JUnit XML for per-test durations.
- Store
timings.jsonsomewhere accessible (S3, gh-pages, the repo). - Before the next run, read it and bin-pack tests across shards (greedy: assign each test to the currently-shortest shard).
- Pass the resulting test list via
--grepor a generated config.
This gets you cloud-quality balancing without paying for it. A 30-minute suite that defaults to 6-minute / 4-minute / 12-minute / 8-minute shards becomes 4 × 7.5-minute shards.
Reports across shards:
- name: Upload blob report
if: always()
uses: actions/upload-artifact@v4
with:
name: blob-report-${{ matrix.shard }}
path: blob-report
merge-reports:
needs: [test]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with: { pattern: blob-report-* }
- run: npx playwright merge-reports --reporter=html ./all-blob-reports
merge-reports combines per-shard blob reports into a single HTML report — the developer experience matches a non-sharded run.
The senior signal: knowing the built-in is sufficient for most teams, naming the duration-balanced extension when needed, and the merge-reports step for unified output.