Sharding
When I hit 800+ e2e tests, parallel workers on one machine stopped being enough. Sharding splits your test suite across multiple CI machines so they run simultaneously. The setup is two lines of YAML — the reporting part takes a bit more work to wire up properly.
The --shard flag
One flag, and each CI job runs its own slice. If I have 4 machines, I run these four commands in parallel — each machine picks up its quarter of the test suite and ignores the rest.
Playwright distributes by test file by default. So if shard 1/4 gets 10 files and shard 2/4 gets 2 files, the timing will be uneven. That's where fullyParallel helps.
Getting even distribution — fullyParallel
Without fullyParallel, Playwright splits at the file level. A file with 50 tests counts the same as a file with 2 tests. One shard ends up doing most of the work while others finish early and sit idle.
With fullyParallel: true, Playwright splits at the individual test level. 400 tests across 4 shards = ~100 tests per shard, regardless of how they're distributed across files. I always use this when sharding.
Blob reporter — collecting results from all shards
Each shard produces its own test report. To get one combined report after all shards finish, I use the blob reporter on CI. It saves raw test data (including traces, screenshots, all attachments) to a zip file that can be merged later.
After downloading all blob reports from CI artifacts into one directory, I merge them into a single HTML report. The blob file names include the shard number so they never conflict.
GitHub Actions setup — matrix strategy
GitHub Actions matrix lets each shard run as an independent job. I define shardIndex as an array and shardTotal as a fixed number — GitHub spawns one job per index value, each referencing both variables.
The artifact upload step has if: ${{ !cancelled() }} — without this, if a test fails (the job fails), the artifact upload is skipped and you have no report to look at. This condition runs the upload even when the job failed.
Then a separate merge-reports job waits for all shards via needs: [playwright-tests], downloads all blob artifacts, and merges them into one HTML report.
Merging reports from different environments
If I run the same tests against staging and production simultaneously, I tag each run with the environment name via TestConfig.tag. The blob report picks up this tag automatically, so the merged report shows which results came from which environment.