GUIDE / automation
How to Run Tests in Parallel with Playwright
Learn how to run tests in parallel with Playwright: workers, sharding, isolation, CI config, flakiness fixes, and a practical checklist for faster suites.
Playwright already parallelizes with worker processes by default; the hard part is keeping tests isolated when many run at once. How to run tests in parallel with Playwright is mostly about workers, fullyParallel, projects, CI sharding, and designing suites that do not share mutable accounts or order-dependent data.
This guide covers how Playwright parallelism works, how to configure workers safely, how to use sharding in CI, how to isolate data and auth, how to debug cross-test interference, and the common mistakes that turn a fast suite into a flaky one. If you are still setting up the tool itself, start with the Playwright tutorial, then come back here to scale execution.
Why Parallel Tests Matter
Serial suites feel fine when you have twenty checks. They become expensive when you have hundreds of end-to-end flows, multi-browser projects, and a team waiting on green builds. Parallel execution shortens feedback loops for pull requests, reduces context switching, and makes broader regression coverage practical.
Parallelism is not free. It multiplies load on the application under test, the database, and external services. It also multiplies any shared-state mistake. A suite that passes serially can fail in parallel because two tests update the same user, claim the same coupon, or rely on leftover cookies. Speed without isolation is just faster flakiness.
Good parallel design gives you three outcomes:
- Shorter wall-clock runtime.
- Stable results across local and CI machines.
- Predictable capacity planning for CI cost.
How Playwright Parallelism Works
Playwright Test uses worker processes. Each worker can run tests independently. By default, Playwright distributes test files across workers. That means two different files can run at the same time, while tests inside one file often run in order unless you opt into full parallelization.
Key concepts:
| Concept | What it does | When you use it |
|---|---|---|
| Workers | Concurrent processes on one machine | Local speed and single CI job speed |
| fullyParallel | Allows tests in the same file to run concurrently | Independent atomic tests |
| Projects | Browser, device, or env variants | Multi-browser matrix |
| Sharding | Splits suite across machines or jobs | Large suites beyond one runner |
| Isolation | Separate contexts, users, data | Required for reliability |
Think of workers as lanes on one road, and shards as separate roads that each have their own lanes. Both reduce total time, but they solve different bottlenecks.
Default Behavior in Simple Terms
- Playwright discovers tests.
- It starts N worker processes.
- It assigns work to free workers.
- Each test usually gets a fresh browser context.
- Results merge into one report.
A fresh context is strong browser isolation. It is not strong application data isolation. If two tests use the same login email against a shared staging API, they can still collide.
How to Run Tests in Parallel with Playwright: Workers Config
The primary control for how to run tests in parallel with Playwright is the workers option in config or CLI.
Config Example
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 1 : 0,
workers: process.env.CI ? 2 : undefined,
reporter: [['list'], ['html', { open: 'never' }]],
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { browserName: 'chromium' } },
{ name: 'firefox', use: { browserName: 'firefox' } },
],
});
Notes:
workers: undefinedlets Playwright choose based on CPU locally.- Fixed
workers: 2in CI is often safer than maxing out a small runner. fullyParallel: trueonly helps if tests are independent.
CLI Overrides
npx playwright test --workers=4
npx playwright test --workers=50%
npx playwright test tests/checkout.spec.ts --workers=1
Use one worker when debugging a race, a shared fixture, or a suspected order dependency. Parallelism is great for green builds. Serial runs are better for root-cause analysis.
fullyParallel: When to Turn It On
fullyParallel changes the unit of scheduling. Without it, Playwright tends to keep tests in a file sequential while still parallelizing across files. With it, independent tests inside a file can also run together.
Turn it on when:
- Each test sets up its own data.
- Auth is handled by isolated storage state or per-test login fixtures.
- Assertions do not depend on previous tests in the file.
- Cleanup is either unnecessary or safely concurrent.
Keep it off (or keep sequential describes) when:
- A file is a deliberate multi-step journey that reuses one user.
- Setup is expensive and intentionally shared across ordered steps.
- You are mid-migration and still cleaning order dependencies.
A practical pattern is full parallel for atomic specs, and a small serial suite for fragile multi-step journeys you have not refactored yet.
import { test, expect } from '@playwright/test';
test.describe.configure({ mode: 'parallel' });
test('creates an order as buyer A', async ({ page }) => {
// unique user and cart
});
test('creates an order as buyer B', async ({ page }) => {
// different unique user and cart
});
Or force serial for one describe:
test.describe.configure({ mode: 'serial' });
test('admin opens settings', async ({ page }) => {});
test('admin updates policy', async ({ page }) => {});
test('admin verifies audit log', async ({ page }) => {});
Serial describes are a temporary bridge, not a long-term architecture. Prefer independent tests when the product allows it.
Isolation Rules That Make Parallelism Safe
1. Prefer Fresh Contexts Over Shared Sessions
Playwright already gives each test a context by default. Do not invent a global browser or page singleton. Custom fixtures should create resources and tear them down without leaking state into other workers.
2. Unique Test Data Every Time
Never hard-code one shared email for all tests if those tests mutate profile, password, or permissions.
const email = `buyer+${Date.now()}-${test.info().workerIndex}@example.test`;
Better still: create users through an API fixture and delete or expire them after the test. Unique suffixes reduce collisions across retries and shards.
3. Isolate Authentication
Common patterns:
| Pattern | Pros | Cons |
|---|---|---|
| Login UI every test | Simple | Slow |
| Storage state per role | Fast | Needs careful file management |
| API token injection | Fast and stable | Requires auth endpoint support |
| Worker-scoped auth | Reuse within worker | Can still collide if same account |
A strong approach is one storage state file per role, plus unique business data under that role. Avoid one global admin account performing destructive cleanup while other tests rely on that admin.
4. Avoid Shared Mutable Fixtures
A seed product with fixed stock can fail when two checkout tests buy the last unit. Prefer creating products, coupons, and inventory per test, or use high-capacity seed data that concurrent buys cannot exhaust.
5. Do Not Depend on Test Order
If test B only works after test A creates "Order 1", you do not have two tests. You have one multi-step script split poorly. Either make them a serial describe, or make each test create what it needs.
Projects and Multi-Browser Parallelism
Projects let you run the same tests under different browser or device settings. Playwright can schedule project work in parallel with workers.
projects: [
{ name: 'Desktop Chrome', use: { browserName: 'chromium' } },
{ name: 'Desktop Firefox', use: { browserName: 'firefox' } },
{ name: 'Mobile Chrome', use: { ...devices['Pixel 7'] } },
]
Watch total load: 3 projects times 4 workers can mean a large number of concurrent browsers. On a small CI runner, that can thrash CPU and create timeouts that look like product bugs. Measure before you max the matrix on every pull request.
A common strategy:
- PR pipeline: Chromium only, high workers, fast feedback.
- Nightly or main branch: full browser matrix, more shards.
That balance keeps CI/CD for test automation useful without burning the budget.
Sharding for Large Suites
When one machine is not enough, shard the suite across jobs.
npx playwright test --shard=1/4
npx playwright test --shard=2/4
npx playwright test --shard=3/4
npx playwright test --shard=4/4
In GitHub Actions, a matrix can express shards cleanly:
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
steps:
- run: npx playwright test --shard=${{ matrix.shard }}/4 --workers=2
Tips for sharding:
- Keep each shard roughly balanced. Extremely long tests can unbalance wall time.
- Merge reports carefully if you need one combined HTML report.
- Ensure seed data and environments can handle concurrent shards, not just concurrent workers.
- Use the same commit SHA and environment variables on every shard.
Workers speed one machine. Shards add machines. Use both when the suite is large.
Designing Tests for Parallel Execution
Atomic Tests Beat Story Tests for Speed
Atomic tests verify one behavior. Story tests walk a long journey. Both have value. For parallel scale, favor many atomic tests plus a small number of critical journeys.
Example atomic cases:
- Add item to cart persists after refresh.
- Invalid coupon is rejected with a clear message.
- Guest cannot open order history.
Example journey:
- Browse, add, checkout, pay, confirm email.
Journeys are slower and more fragile. Keep them few and high value.
Use API Setup to Reduce UI Drag
Create prerequisites through API calls, then verify UI behavior. Setup becomes faster and less flaky, which makes parallel workers more efficient.
test('shows shipped status on order details', async ({ page, request }) => {
const order = await request.post('/api/test/orders', {
data: { status: 'shipped', userId: process.env.BUYER_ID },
});
const { id } = await order.json();
await page.goto(`/orders/${id}`);
await expect(page.getByTestId('order-status')).toHaveText('Shipped');
});
Prefer Deterministic Assertions
Parallel environments can have variable latency. Use Playwright auto-waiting assertions instead of fixed sleeps. Sleeps waste worker time and still fail under load.
Debugging Parallel Failures
When a test fails only under parallel workers, treat it as a concurrency bug until proven otherwise.
Reproduce With Controlled Concurrency
npx playwright test --workers=1
npx playwright test --workers=4
npx playwright test path/to/suspect.spec.ts --workers=4 --repeat-each=5
If it passes at one worker and fails at four, look for shared state.
Inspect Worker Index and Timing
Log test.info().workerIndex, timestamps, and entity ids. Collisions often show as two tests mutating the same id within milliseconds.
Use Traces and Videos Selectively
Traces are excellent, but enabling them for every parallel test can slow IO. Prefer on-first-retry in CI. Locally, open the failing trace and check whether the app state at failure matches what the test expected.
Quarantine Carefully
A quarantine list can keep main green while you fix root causes. It is not a strategy. If you need a deeper playbook for intermittent failures, use how to fix flaky tests.
CI Capacity Planning
Parallel tests change the shape of infrastructure cost.
| Lever | Effect on time | Effect on cost/risk |
|---|---|---|
| More workers on one job | Faster until CPU/IO saturates | Can increase flakiness and timeouts |
| More shards | Near-linear speedup if balanced | More minutes billed, more env load |
| Fewer projects on PR | Much faster PR feedback | Less browser coverage on PR |
| API setup instead of UI setup | Faster every worker | Requires test APIs or seed tools |
| Better selectors and waits | Fewer retries | Less waste under load |
A simple tuning loop:
- Measure baseline duration with current workers.
- Increase workers until duration stops improving or flakiness rises.
- Add shards when one job remains too slow.
- Move long-tail browsers to nightly.
- Optimize the slowest 10 tests before buying more CI.
Sample Architecture for a Parallel-Friendly Suite
tests/
auth/
login.spec.ts
logout.spec.ts
checkout/
coupon.spec.ts
payment.spec.ts
account/
profile.spec.ts
fixtures/
users.ts
api.ts
auth.ts
auth/
buyer.json
admin.json
Guidelines:
- Keep files focused so failures localize quickly.
- Put reusable isolation logic in fixtures.
- Store role auth state outside specs.
- Avoid global mutable variables in helper modules.
Example Auth Fixture Pattern
import { test as base } from '@playwright/test';
type Fixtures = {
buyerPage: import('@playwright/test').Page;
};
export const test = base.extend<Fixtures>({
buyerPage: async ({ browser }, use) => {
const context = await browser.newContext({
storageState: 'auth/buyer.json',
});
const page = await context.newPage();
await use(page);
await context.close();
},
});
Each test gets a new context from shared storage state. That is usually safe for read-heavy flows. For write-heavy account settings, create a unique user per test instead of reusing one buyer forever.
Common Mistakes When Running Parallel Playwright Tests
Mistake 1: Assuming Browser Isolation Equals Data Isolation
A fresh context does not create a fresh database row. Two workers can still fight over the same order id.
Mistake 2: Using One Shared Test Account for Everything
Password changes, 2FA challenges, cart pollution, and permission flips will appear as random failures.
Mistake 3: Maxing Workers Without Measuring
Eight workers on a two-core CI runner can make every test slower and flakier. Measure wall time and failure rate together.
Mistake 4: Turning on fullyParallel Before Cleaning Dependencies
If tests were written as ordered scripts, full parallel will expose that immediately. Refactor first, or enable file-level parallel only.
Mistake 5: Seeding a Tiny Inventory for Checkout Tests
Stockouts under concurrency look like product defects. Seed ample inventory or create stock per test.
Mistake 6: Ignoring External Rate Limits
Email providers, payment sandboxes, and third-party APIs may throttle concurrent traffic. Stub, mock, or isolate those dependencies in automated runs when possible.
Mistake 7: Retrying Without Root Cause Work
Retries can hide race conditions. One CI retry is often reasonable. Endless retries teach the suite to lie.
Checklist: Ready for Parallel Execution?
Use this before you scale workers in CI:
- Tests do not depend on execution order.
- Unique data strategy exists for users, orders, and mutable records.
- Auth strategy is documented and not destructive across workers.
- No global mutable shared state in helpers.
- Local run with multiple workers is stable.
- CI worker count matches runner capacity.
- Shards are balanced if used.
- Critical journeys are few and intentionally serial if needed.
- Traces and artifacts are configured for failed runs.
- Slowest tests are known and optimized.
Worked Example: Speeding Up a 20-Minute Suite
Imagine a suite that takes 20 minutes with one worker:
- 120 UI tests
- One Chromium project on pull requests
- Shared staging environment
- Average test duration about 10 seconds
Possible plan:
- Fix four known shared-account collisions.
- Set
workers: 4locally and in CI. - Enable
fullyParallelonly for atomic folders. - Convert setup for 30 tests from UI to API.
- Move Firefox and WebKit to nightly shards.
Expected shape after improvements:
- PR Chromium suite near 6 to 8 minutes.
- Nightly multi-browser suite sharded into four jobs.
- Flake rate drops because data collisions were removed first.
Notice the order: isolation first, then concurrency. Teams that reverse that order usually spend weeks chasing ghosts.
Parallelism Compared Across Frameworks
If you are choosing tools as well as configuring them, parallelism model is one decision factor.
| Topic | Playwright | Cypress | Selenium Grid |
|---|---|---|---|
| Local parallel default | Strong file/worker model | Historically more constrained | Depends on runner setup |
| Multi-browser | First-class projects | More limited native matrix | Broad, infra heavy |
| Isolation unit | Browser context | Spec/browser instance patterns | Session/node |
| CI scaling | Workers + shards | Parallelization strategies vary | Grid nodes |
| Learning curve for isolation | Moderate | Moderate | Higher ops load |
For a fuller tool comparison, see Selenium vs Playwright vs Cypress.
Practice Path
Reading config flags is not enough. Practice writing tests that stay green under concurrency:
- Write five independent Playwright tests for one feature.
- Run them with one worker, then four workers.
- Introduce a deliberate shared user bug and observe the failure mode.
- Fix isolation with unique data and re-run.
- Add a CI shard matrix once local parallel is stable.
You can sharpen those skills in competitive, scenario-based practice on QABattle battles, then apply the same isolation discipline in your product suite.
Final Workflow: From Serial to Parallel
- Make tests independent and deterministic.
- Remove shared mutable accounts and fixed inventory assumptions.
- Add API setup for expensive preconditions.
- Enable multiple workers and measure.
- Turn on
fullyParallelwhere tests are truly atomic. - Keep a small serial pocket only for intentional journeys.
- Add shards when one machine cannot meet the time budget.
- Keep flake monitoring on, and fix root causes quickly.
Learning how to run tests in parallel with Playwright is really learning how to design trustworthy automated coverage under concurrency. Workers and shards are levers. Isolation is the foundation. Get the foundation right, and parallel execution becomes one of the highest leverage upgrades you can make to an automation suite.
Parallel Tests and Test Tags
As suites grow, not every test should run at the same parallel intensity on every pipeline event. Playwright supports tagging and grep filters so you can shape what runs under high concurrency.
test('checkout total updates @smoke @checkout', async ({ page }) => {
// ...
});
npx playwright test --grep @smoke --workers=4
npx playwright test --grep-invert @slow --workers=4
A practical model:
| Pipeline event | Filter | Workers / shards |
|---|---|---|
| Pull request | @smoke | Higher workers, one browser |
| Merge to main | full suite minus @nightly | Workers + 2-4 shards |
| Nightly | full including @slow | More shards, multi-browser |
Tags prevent the false choice between "run everything slowly" and "skip valuable coverage." Parallelism works best when the batch of work is intentional.
Environment Readiness Before Scaling Workers
If the application under test is cold starting, four workers can create a thundering herd of logins and seed calls. Stabilize the environment first:
- Wait for health and readiness endpoints.
- Warm critical paths once before the suite.
- Confirm seed jobs finished.
- Then start parallel workers.
# conceptual pre-step in CI
curl -f "$BASE_URL/health"
npx playwright test --project=setup
npx playwright test --workers=4
Parallel failures that vanish after a re-run often point to readiness issues, not product regressions. Fix readiness, then trust the suite.
Reporting Under Parallel Execution
When many workers finish close together, reports must still answer simple questions:
- Which tests failed?
- Which shard and project?
- What was the first useful error?
- Where is the trace?
Use list plus HTML reporters in CI, upload blob or zip artifacts per shard, and merge if your pipeline supports it. A fast suite with unreadable failures wastes the time it saved.
Team Conventions That Keep Parallel Suites Healthy
Write down conventions so new contributors do not reintroduce serial assumptions:
- No shared mutable accounts in new tests.
- Unique suffix required for created entities.
- Prefer API setup helpers over UI setup chains.
- Serial mode requires a code comment explaining why.
- New
@slowtags need owner approval for PR inclusion. - Flake rate above a threshold blocks "just add retries" shortcuts.
Conventions matter more than any single config flag. How to run tests in parallel with Playwright is a team skill as much as a framework feature.
Final Recap Table
| Lever | Primary benefit | Main risk if misused |
|---|---|---|
| workers | Faster single-machine runs | CPU thrash, flakes |
| fullyParallel | Faster file-local suites | Hidden test coupling |
| projects | Multi-browser coverage | Multiplied load |
| shards | Horizontal scale | Unbalanced duration |
| isolation design | Trustworthy results | None if done well |
Start with isolation, measure, then scale. That order is the difference between a parallel suite that saves hours and one that burns them in re-runs.
FAQ
Questions testers ask
How do you enable parallel testing in Playwright?
Playwright runs files in parallel by default using worker processes. Set workers in playwright.config.ts, use fullyParallel true to split tests inside a file, and avoid shared mutable state so concurrent runs stay reliable.
What is the difference between workers and sharding in Playwright?
Workers run multiple tests at the same time on one machine. Sharding splits the suite across machines or CI jobs, each with its own workers. Use workers for local and single-job speed, and sharding when one machine is not enough.
Why do parallel Playwright tests fail intermittently?
Common causes are shared accounts, fixed test data, race conditions on the same records, order-dependent setup, and hard-coded ports. Isolate users and data per test, create unique records, and stop assuming suite order.
How many Playwright workers should I use in CI?
Start with workers equal to available CPU cores or slightly below, then measure duration and flakiness. Many CI jobs run well with 2 to 4 workers per container. Scale with more shards when one job is still too slow.
Does fullyParallel change how my tests must be written?
Yes. fullyParallel allows tests in the same file to run concurrently, so they must not share browser state, cookies, or sequential data dependencies. Prefer independent tests and shared fixtures that provision isolated resources.
Can Playwright run parallel tests across browsers?
Yes. Project configuration can run Chromium, Firefox, and WebKit projects in parallel. Combine projects with workers carefully, and keep browser-specific flakiness isolated so one project does not hide another.
RELATED GUIDES
Continue the route
Playwright Tutorial: End-to-End Testing from Scratch
Playwright tutorial for beginners: install, write your first test, TypeScript setup, codegen, fixtures, API testing, debugging, and CI tips in one guide.
Flaky Tests: Causes and How to Fix Them
Learn how to fix flaky tests with root cause analysis, stable waits, quarantine strategy, CI retries policy, and practical Playwright examples.
CI/CD for Test Automation with GitHub Actions
Learn CI/CD for test automation with GitHub Actions: Playwright workflows, reports, sharding, and PR vs nightly pipeline strategies that scale.
Selenium vs Playwright vs Cypress in 2026
Compare Selenium vs Playwright vs Cypress in 2026 for speed, browsers, CI, flakiness, ecosystem, and which web automation framework beginners should choose.