Why is the Playwright job so slow in CI
A slow Playwright job usually means the suite is running as one unsharded process, waiting on fixed sleeps instead of real readiness signals, and uploading every trace and video whether the run passed or not. Check sharding, sleeps, and artifact capture before assuming the tests themselves are slow.
AI agents find the cause
StarSling agents run this diagnosis against your workflows and run history, identify which cause is yours, and open the fix as a reviewable PR.
Symptoms
When a Playwright job dominates the workflow's wall clock, the browsers, the test count, and the assertions are rarely the bottleneck on their own. What usually happens is that a single job runs the entire suite sequentially in one process, so the job's duration is the sum of every test instead of the slowest shard. Inside that run, page.waitForTimeout calls and container startup sleeps add fixed delays that have nothing to do with how fast the page actually responded. On top of that, trace and video capture set to on records and uploads artifacts for every test, including the ones that passed, and a cold browser-binary cache reinstalls Chromium, Firefox, and WebKit from scratch on every run. The causes below are ordered by how much of the job's time they typically consume: artifact capture and browser installs first because they are the cheapest to confirm and fix, then sharding because it changes the job's structure, then sleeps and healthchecks because they hide inside test and service code rather than the workflow file, and dependency caching last because it affects setup time rather than the test run itself.
The Playwright job takes far longer in CI than the same suite takes to run locally.
The workflow run summary shows one long e2e job instead of several shorter parallel ones.
Every run, including ones where every test passes, uploads a full set of trace and video artifacts.
The job log shows long gaps where nothing appears to be happening before a test or a service starts responding.
The
npx playwright installstep takes a noticeable chunk of the job on every run.
How to diagnose it
Open the workflow run in the Actions UI and read the Playwright job's total duration next to the other jobs in the run; if it is the longest by a wide margin, it is the critical path worth diagnosing.
Expand the job's steps and note how long
npx playwright installand the dependency install step each take before any test starts.Read the job's YAML: run
grep -n 'shard\|matrix' .github/workflows/*.ymlfor the Playwright job to see whether it is already split across parallel legs.Compare the
upload-artifactstep's size across a few recent runs; if passing runs upload traces and videos as large as failing runs, capture is not conditioned on failure.Grep the test source for fixed waits:
grep -rn 'waitForTimeout' e2e/ tests/ --include='*.spec.ts'and note the count and the millisecond values.Check whether any service the tests depend on (database, mock API) starts with a
sleepstep or ahealthcheck:block in its container definition.
Likely causes
Ordered by how often each one turns out to be the answer. Confirm a cause with its check before you change anything.
Traces and videos captured on every run, not just failures
Playwright's trace and video recorders write to disk while tests run, and the
upload-artifactstep then has to compress and transfer whatever they produced. Whentraceorvideoin the Playwright config is set tooninstead ofon-first-retryorretain-on-failure, that recording and upload cost is paid on every run, including the runs that pass. A barenpx playwright install --with-depswith no browser argument also installs Chromium, Firefox, and WebKit even when the suite only targets one, adding to the same step.Fix: Playwright artifact captureConfirm it
Run
grep -n 'trace:\|video:\|screenshot:' playwright.config.*and check whether the values are'on'(records every run) versus'on-first-retry'or'retain-on-failure'. Then rungrep -n 'playwright install' .github/workflows/*.ymland check whether a specific--with-deps chromium(or similar) is passed.The suite runs as one job instead of parallel shards
Playwright supports splitting a suite across machines with
--shard=<index>/<total>, but a job that runs the full suite in a single process is bounded by the sum of every test's duration rather than the slowest shard. This is the largest single lever on a Playwright job's wall clock because the job cannot finish faster than its own test count allows, no matter how fast individual pages respond.Fix: Test shardingConfirm it
Run
grep -n 'shard' .github/workflows/*.yml playwright.config.*and check whether amatrixwithshardor--shard=${{ matrix.shard }}/${{ strategy.job-total }}is present. If it is absent, the job is running the entire suite as one unit.Fixed sleeps inside the test source, not the workflow
page.waitForTimeout(),cy.wait(<ms>), and rawsetTimeout/sleepcalls embedded in test files add fixed delays regardless of how quickly the page actually became ready. Because these live in test source rather than workflow YAML, they do not show up when scanning.github/workflows/alone, and they accumulate across every spec file that uses them.Fix: Polling waits, not fixed sleepsConfirm it
Run
grep -rn 'page\.waitForTimeout(\|waitForTimeout([0-9]' e2e/ playwright/ tests/ --include='*.spec.ts' --include='*.test.ts'and sum the delays found; a suite with dozens of these calls can lose minutes per run to sleeps alone.The job sleeps for a fixed time before a dependent container is ready
A workflow step using
sleep Nto wait for a database or backend container to accept connections has to guess a duration long enough for the slowest case, so it wastes time on every run where the container starts faster than the guess. This is separate from sleeps in test source: it appears directly in the workflow file ordocker-compose.yml, ahead of the test job actually starting.Fix: Container healthchecksConfirm it
Run
grep -rn 'sleep [0-9]' .github/workflows/ docker-compose*.ymland check whether the container the Playwright job depends on has ahealthcheck:block; if it only has asleepbefore the test step, this is the cause.No caching for npm/pnpm dependencies or the Playwright browser binaries
The Playwright job installs project dependencies and then downloads browser binaries before any test runs, and neither install is fast without a cache. Without
actions/cache(orsetup-node's built-in cache) targeting the package manager's store, and without a cache targeting the Playwright browser binary directory, both steps repeat their full download and install cost on every single run.Fix: Dependency cachingConfirm it
Run
grep -n 'actions/cache\|setup-node.*cache' .github/workflows/*.ymlin the job that runs Playwright, and separately check whether the cache key or path covers the Playwright browser binaries (typically under the OS cache home) rather than onlynode_modules.
Hand it to an agent
Hand this prompt to your coding agent (Claude Code, Cursor, and the like) to run this diagnosis against your repository and report which cause it found.
Diagnose why the Playwright end-to-end job dominates this repository's CI wall clock. Do not change anything yet. First, read the workflow file(s) under `.github/workflows/` that run Playwright and note the job's total duration relative to other jobs. Then check, in this order: (1) whether `playwright.config.*` sets `trace` or `video` to `'on'` instead of `'on-first-retry'`/`'retain-on-failure'`, and whether `upload-artifact` runs unconditionally; (2) whether the job uses `--shard`/a matrix to split tests across parallel legs, or runs the full suite in one process; (3) whether test files under `e2e/`, `tests/`, or `playwright/` contain `page.waitForTimeout()` or similar fixed sleeps, and how much total time they add; (4) whether any dependent container is started with a `sleep` instead of a `healthcheck:`; (5) whether dependency installs and the Playwright browser binary download are cached with `actions/cache` or `setup-node`'s cache option. Report which of these five causes you found evidence for, in order of estimated time contribution, before proposing or making any change.Go further
Find the cause, fix them all, or keep them fixed.
You have the prompt that works out which cause applies. Here is how much further you can take it, each step doing more for you than the last.
Find the cause
Copy the diagnosis prompt above
Hand it to your coding agent to confirm which cause applies in your repo. It reports back what it found and changes nothing.
Fix them all, once
Install the ci-speedup skill
One prompt audits your whole repo against all 73 ci-speedup patterns (these 6 plus 67 more) and hands your agent every fix at once. Open source, MIT, runs locally.
Keep it fixed, forever
Install the StarSling GitHub App
Connect GitHub and the fixes stay applied as your CI evolves, with agents that keep inspecting your workflows and opening optimization PRs you review.
Sources
1Playwright: Continuous Integration (opens in new tab)
2Playwright: Sharding (opens in new tab)
3GitHub Actions: Caching dependencies to speed up workflows (opens in new tab)
4GitHub Actions: Run variations of jobs in a workflow (opens in new tab)
Last updated 2026-08-31