Running tests in parallel
Run many Playwright tests at once, keep the results together, and know what happens when you ask for more than you have.
Playwright already runs tests in parallel — it has workers built in, and each one opens its own session with us. Usually there is nothing to turn on.
Two things to get right: how many workers, and keeping the results together.
1. Choose your worker count
npx playwright test --workers=5Or in playwright.config.ts:
export default defineConfig({
workers: 5,
fullyParallel: true,
});Playwright defaults to half your CPU cores locally and one worker in CI, so a CI run is often serial without anyone noticing. Set it explicitly.
2. Give every worker the same runId
Five workers means five sessions. Without a runId that is five separate runs in your dashboard, which tells you nothing about whether your tests passed.
Use a value your CI already has — GITHUB_RUN_ID, BUILD_NUMBER, CI_PIPELINE_ID:
"testkase:options": {
runId: process.env.GITHUB_RUN_ID ?? "local",
}runId yourself.build does not group anything. It is a label. Sessions that share a build but no runId are still separate runs.3. How many at once
| Limit | |
|---|---|
| Paid plans | One session per thread, 1 to 20 threads |
| Free tier | No thread limit — your remaining free minutes are the only ceiling |
| Over your limit | Waits for one of your threads to free up, then refuses if none does |
Going over your limit no longer fails straight away. The session waits for one
of your own threads, and only gives up if none comes free in time — arriving
as a WebSocket close 4429:
Waited, but all 5 of your parallel sessions were busy the whole time.
Close one, or raise your plan's parallel limit.So a run with more workers than threads will mostly still finish; the extra
workers wait their turn. Keep --workers at or below your thread count
anyway, because waiting is slower than sizing the run correctly — and
Playwright will not retry a connection refused this way.
How long it waits is the same maxQueueWaitSeconds described below, and the
two waits share it: time spent waiting for a thread is time not left for
waiting for a machine.
4. Why a session still waits sometimes
Being under your own limit does not mean a machine is free. Our machines have their own limit, shared with everyone, and you wait when every machine matching your request is busy. That wait is maxQueueWaitSeconds — 5 minutes by default, up to 9 minutes — and it is not billed.
Two things help more than raising that number:
Ask for a mix. Five workers all asking for one macOS version and Safari draw on a handful of machines. The same five across Chrome, Edge and Firefox on two operating systems draw on many more.
Leave room for cleaning up. A session that is still ending still holds its machine. Fire a second run immediately at the same narrow target and it queues behind the run before it.
The full picture — thread bands, per-machine ceilings, free minutes — is in Reference.
Do you also need to mark tests?
With workers, each session runs one test, so the session name is enough to read the run at a glance.
Marking tests is for the other shape — one connection running several tests in a row.
