How to Speed Up Playwright Tests
Find what is slowing down your Playwright suite, then tune workers, parallelism, sharding, setup, and diagnostics without sacrificing reliable results.
To speed up Playwright tests, first measure a representative run, then tune worker concurrency, remove unnecessary serial execution, and shard the suite across machines if one runner is still the bottleneck. Reduce avoidable browser setup and routine trace collection, too. Keep tests independent: more concurrency can make a run slower or flaky when tests compete for CPU, backend capacity, accounts, files, or other shared state.
Playwright Test runs test files in parallel by default. Tests within a file run in order unless you enable test-level parallelism. The right worker count depends on the machine and the system under test; the documented default of half the logical CPU cores is a starting point, not a guaranteed optimum. [Playwright parallelism] [Playwright configuration]
1. Measure the current run
Before changing configuration, establish where the time goes. Compare runs on the same runner type, with the same browser projects, test selection, and reporting settings. Repeat runs to avoid mistaking ordinary variation for an improvement. The documentation describes tuning mechanisms but does not establish a universal speedup for any one setting.
- Record the total duration and note the runner’s CPU and memory pressure.
- Check whether time is concentrated in a few slow tests, browser startup, app or database contention, or time spent waiting for external services.
- Compare the configured worker count with actual resource use. A busy application or database can be the limit even when the runner still has spare CPU.
- Inspect traces for slow actions, navigation, and network requests when a test’s duration is unclear. Use tracing selectively; collecting a trace for every test adds overhead.
2. Tune workers on one machine
Set workers in Playwright’s configuration to control concurrent worker processes. Try a conservative count first, then increase it in measured steps while watching duration, resource pressure, backend load, and flaky outcomes. More workers do not guarantee a faster run: they can contend for resources or overload a shared dependency.
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
// Start with a measured value for this runner. Tune it against the
// machine, application, and external services used by the suite.
workers: process.env.CI ? 4 : undefined,
use: {
baseURL: 'http://127.0.0.1:3000',
trace: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
});
Playwright’s documented default is half the logical CPU cores. Leaving workers unset uses that default; setting it explicitly makes the concurrency choice visible and repeatable. In CI, start with a value suited to the runner rather than assuming a developer workstation’s best setting applies. [Configuration reference]
When to reduce workers
- The runner runs out of memory or spends substantial time swapping.
- Tests overload a development server, database, rate-limited API, or other shared service.
- Failures appear only under concurrency, suggesting shared data or resource collisions.
Lowering worker count can improve reliability and even total duration when contention is the bottleneck. It is reasonable to use different worker counts for local development and CI, provided both are configured deliberately.
3. Enable more test-level parallelism safely
By default, Playwright runs files in parallel but runs tests within a file in order. To allow tests in a file to run concurrently, use fullyParallel or mark a group as parallel. Do this only after tests no longer depend on ordering or mutable shared state. [Parallelism guide]
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
fullyParallel: true,
});
Alternatively, enable parallel mode for a particular group:
import { test, expect } from '@playwright/test';
test.describe.configure({ mode: 'parallel' });
test('creates a draft', async ({ page }) => {
await page.goto('/drafts/new');
await expect(page.getByRole('heading', { name: 'New draft' })).toBeVisible();
});
test('opens the help page', async ({ page }) => {
await page.goto('/help');
await expect(page.getByRole('heading', { name: 'Help' })).toBeVisible();
});
These example tests use independent pages. In a real suite, the backend records they create must also be independent. Each test gets an isolated browser context for cookies and browser storage, but that does not isolate database rows, shared user accounts, files, or application-wide settings. [Browser contexts] [Parallelism guide]
Prevent collisions before increasing concurrency
- Backend records: include a unique test identifier in names or keys. For example, derive a record label from
testInfo.testIdrather than reusing a fixed email or title. - Files and artifacts: use
testInfo.outputPath()to give each test its own output location. - Accounts: avoid having concurrent tests mutate the same account unless the application and test design explicitly support it.
- Module state: do not rely on in-memory state shared between tests or workers.
- Cleanup: make cleanup safe when tests run in parallel and when a test fails before reaching its final step.
Keep ordering-dependent work in a sequential group or redesign it as independent setup and assertions. Playwright recommends isolated tests because they can be run and retried independently. [Retries]
4. Shard large suites across CI machines
If one runner has reached a useful worker level and remains the bottleneck, run shards on separate CI machines. Each machine runs a portion of the suite using --shard=x/y. Shards add machine capacity, but total elapsed time depends on startup overhead, runner availability, and how evenly work is distributed. [Sharding guide]
# Run these as separate CI jobs, replacing the shard index for each job.
npx playwright test --shard=1/4
npx playwright test --shard=2/4
npx playwright test --shard=3/4
npx playwright test --shard=4/4
With ordinary file-level scheduling, a shard receives files, so a few unusually long files can leave other machines idle. Fully parallel execution allows the sharding system to distribute tests more finely in versions that support that behavior. Check the sharding guidance for the Playwright version installed in your project, especially when relying on test-level balancing. [Sharding guide for next]
Use separate CI jobs with the same test command and configuration, changing only the shard index and total. Merge or retain reports according to your CI reporting setup. Before adding shards, check that the application and shared dependencies can handle the combined load.
5. Trim browser and CI setup time
Install only the browser engines a job needs, and select only the intended project when the job is responsible for a subset of browser coverage. This can reduce browser download and disk setup work. Keep the project matrix aligned with the coverage the team requires; skipping a required browser is not a valid speed improvement. [Best practices] [CLI options]
# Install just the browser used by this job
npx playwright install chromium
# Run only one configured project
npx playwright test --project=chromium
Also avoid repeated installation or build steps when the CI system can reuse appropriate cached dependencies. Cache configuration is CI-provider-specific, so follow that provider’s documentation and ensure cached browser binaries remain compatible with the installed Playwright version.
6. Keep diagnostics useful without collecting everything
Traces help diagnose failures by recording action timings, DOM snapshots, and network activity. Playwright recommends tracing on the first retry in CI; tracing every test can be performance-heavy. Open saved traces in Trace Viewer when investigating a failure. [Trace Viewer] [Best practices]
// In playwright.config.ts
use: {
trace: 'on-first-retry',
},
A retry is a diagnostic and resilience mechanism, not a way to make the underlying test faster. Playwright classifies outcomes as passed, flaky, or failed. If a retry makes a run pass, investigate the instability rather than treating the retry as a performance fix. Serial groups retry together, another reason to prefer independent tests where practical. [Retries]
7. Shorten feedback while debugging
Targeted commands help developers get a result sooner, and a failure limit can stop a clearly broken CI run. These options save feedback or wasted-work time; they do not reduce the runtime of a complete successful suite. [CLI reference]
# Re-run tests that failed in the previous run
npx playwright test --last-failed
# Stop after the first failure
npx playwright test --max-failures=1
# Run one file or one project while iterating
npx playwright test tests/account.spec.ts --project=chromium
8. A practical tuning order
- Baseline: record repeated runs on the target runner with unchanged projects and reporting.
- Find the bottleneck: distinguish long tests and setup from CPU, memory, app, database, or external-service contention.
- Tune workers: start from the documented default or a conservative explicit count, then increase or decrease in measured steps.
- Remove unsafe serialization: identify independent tests within files and make their data and artifacts unique before enabling parallel mode.
- Shard: distribute a large suite over CI machines if one runner is still limiting completion time; check for uneven file sizes and version-specific test balancing.
- Trim setup and diagnostics: install only needed browsers and keep routine tracing proportionate.
- Recheck reliability: compare flaky outcomes and external-system load as well as elapsed time.
9. Common problems and fixes
| Symptom | Likely cause | What to do |
|---|---|---|
| More workers make the run slower | CPU or memory contention, or an overloaded app or backend | Reduce workers and measure again; identify which shared resource is saturated. |
| Tests pass alone but fail in a full run | Shared database records, account state, files, module state, or application settings | Give tests unique data and output paths; remove ordering dependencies or keep dependent work sequential. |
| One shard finishes much later than the others | Uneven test-file sizes or a small number of slow tests concentrated on a shard | Review file and test durations, split oversized files where useful, and check whether the installed version supports finer balancing with fully parallel mode. |
| CI spends a long time downloading browsers | The job installs browser engines it does not use, or repeats setup unnecessarily | Install only the engines needed by that job and review compatible CI caching options. |
| Failures become harder to investigate after reducing traces | Diagnostics were reduced too far for the team’s needs | Use on-first-retry for CI diagnosis, or collect more trace data temporarily while investigating a specific issue. |
| A run passes only after retries | The test is flaky or affected by timing or shared state | Inspect the trace and failure classification, then fix the cause; do not count retries as a speed optimization. |
| A targeted command appears faster, but CI duration is unchanged | The command ran fewer tests, so it shortened feedback rather than a full suite | Compare complete runs when evaluating suite performance. |
10. Costs, reliability, and trade-offs
More workers use more concurrent resources on one runner. More shards require additional machines and incur their startup and execution costs. The best choice depends on runner availability and cost, shard balance, external-system capacity, and how soon results need to return. The Playwright documentation does not promise linear scaling or a project-independent speedup.
Keep required browser coverage intact when selecting projects or installing browsers. Keep isolation intact when increasing concurrency. A faster run that hides failures, overloads a dependency, or relies on retries is not a trustworthy improvement.
Or skip the browser setup
If a test workflow also needs screenshots of pages for review or visual checks, ScreenshotNeo is a website screenshot API and MCP server for developers. Its one-request API returns a PNG, JPEG, WebP, or PDF. See the API documentation.
# cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
# Python
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)
# Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie banners, newsletter popups, and chat widgets are removed before capture; each cleanup step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Response headers report the page verdict and billing status.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for AI agents and MCP clients. - 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for 1,000 free screenshots a month, with no card required.
FAQ
What is the default number of Playwright workers?
The configuration reference documents a default of half the logical CPU cores. Treat it as a starting point and tune against the runner and application.
Should every test run in parallel?
Only when it is independent of other tests’ order and shared state. Browser contexts isolate cookies and browser storage, not your backend data or files.
Do retries make tests faster?
No. Retries can help classify and investigate flaky behavior, but they may add work and do not fix the underlying cause.
Does --last-failed speed up the complete suite?
No. It reruns prior failures to shorten a debugging loop; a full successful run still needs its full test selection.


