PRACTICAL GUIDE / playwright workerIndex parallelIndex
Deterministic Resource Allocation with Playwright Worker Indexes
Allocate databases, accounts, ports, and tenants safely with Playwright parallelIndex, workerIndex, worker fixtures, and restart-aware cleanup.
In this guide11 sections
- Model a worker as a replaceable execution slot
- Build a complete namespace, not just a numeric suffix
- Allocate through worker-scoped fixtures
- Separate worker setup from per-test reset
- Allocate fixed account pools without silent reuse
- Reserve ports and filesystem paths by slot
- Design for worker replacement and abrupt termination
- Diagnose allocation failures systematically
- Weigh reuse against isolation
- Operational resource-allocation checklist
- Implement deterministic allocation in stages
What you will learn
- Model a worker as a replaceable execution slot
- Build a complete namespace, not just a numeric suffix
- Allocate through worker-scoped fixtures
- Separate worker setup from per-test reset
Parallel tests become unreliable when they share a resource that was designed for one writer. Two workers updating the same account, database schema, download directory, or message queue can produce failures that disappear when the suite runs alone. Playwright exposes worker and parallel indexes so a framework can allocate one deterministic resource namespace per concurrent slot.
The indexes are not interchangeable. parallelIndex is the stable slot from zero to the configured worker count minus one. workerIndex identifies a particular process lifetime and changes when Playwright replaces a worker after failure. Resource allocation should follow that lifecycle instead of treating both numbers as convenient unique suffixes.
Model a worker as a replaceable execution slot
The Playwright parallelism documentation states that a restarted worker receives the same parallelIndex and a new workerIndex. That makes parallelIndex suitable for selecting a database, port range, tenant, or account from a bounded pool. The replacement process returns to the same logical slot.
Use workerIndex when the identity should change with the process: log correlation, trace labels, diagnostic directories, or proving that a retry ran in a new worker. If a pool has four resources and code indexes it with workerIndex, a replacement worker can exceed the pool boundary because that value is globally increasing rather than constrained to four slots.
Animated field map
Restart-safe worker resource lifecycle
Bind durable resources to a parallel slot while treating each worker process as replaceable.
01 / worker process
Worker process
Start one isolated process with a unique lifetime identity.
02 / parallel slot
Stable parallel index
Resolve the bounded execution slot that survives replacement.
03 / resource namespace
Resource namespace
Map the run, shard, project, and slot to isolated resources.
04 / test batch
Test file batch
Reuse worker fixtures only among tests allowed to share the slot.
05 / resource release
Resource release
Reset external state and make cleanup safe after partial failure.
Build a complete namespace, not just a numeric suffix
parallelIndex is unique only among workers running in one Playwright invocation. Separate shard jobs can each have parallel slot zero. Browser projects may also execute the same test inventory concurrently. If those jobs point at one backend, tenant-0 will collide.
Compose external resource identity from the dimensions that can overlap: CI run, shard, Playwright project, and parallel slot. Sanitize names and keep them within provider limits. A typical namespace might encode a short run token, shard-2, webkit, and slot-1.
Do not include retry number in a durable resource name by default. A retry should normally reuse the logical slot after reset so allocation remains bounded. Add attempt identity only for immutable evidence, such as a log archive, where preserving each attempt is the goal.
Allocate through worker-scoped fixtures
Worker-scoped fixtures run once per worker process and can be reused across multiple test files when their worker fixture environments match. They are a natural owner for an isolated database, leased account, local service, or queue namespace. Setup occurs before the first consumer; teardown occurs when that worker ends.
import { test as base } from '@playwright/test';
type DatabaseLease = {
schema: string;
connectionUrl: string;
};
type WorkerFixtures = {
databaseLease: DatabaseLease;
};
export const test = base.extend<{}, WorkerFixtures>({
databaseLease: [
async ({}, use, workerInfo) => {
const run = process.env.CI_RUN_ID ?? 'local';
const shard = process.env.CI_SHARD_INDEX ?? '0';
const project = workerInfo.project.name.replace(/[^a-z0-9]+/gi, '-');
const schema = `pw_${run}_${shard}_${project}_${workerInfo.parallelIndex}`;
const lease = await provisionIsolatedSchema(schema);
try {
await use(lease);
} finally {
await releaseIsolatedSchema(schema);
}
},
{ scope: 'worker' },
],
});
export { expect } from '@playwright/test';The fixture function receives workerInfo, which carries both indexes and the project. Its finally block attempts cleanup even when a test fails. The provisioning function should also detect and reconcile a namespace left by an abruptly terminated prior process.
Separate worker setup from per-test reset
One schema per worker prevents cross-worker collisions, but tests inside that worker can still contaminate each other. Decide what is safe to reuse. A database connection and empty schema may be worker-owned, while records should be reset or created uniquely for each test.
Use a test-scoped fixture that depends on the worker lease and restores a known baseline. This keeps expensive infrastructure allocation at worker scope while making scenario state independent.
import { test as workerTest } from './worker-test';
type TestFixtures = {
accountId: string;
};
export const test = workerTest.extend<TestFixtures>({
accountId: async ({ databaseLease }, use, testInfo) => {
await resetSchema(databaseLease.connectionUrl);
const accountId = await createAccount(databaseLease.connectionUrl, {
label: `${testInfo.title}-${testInfo.retry}`,
});
await use(accountId);
},
});Resetting before the test is often safer than relying only on after-test cleanup because an interrupted process may skip ordinary completion. Cleanup after the test can still remove expensive side effects, but setup must be capable of establishing its own precondition.
Allocate fixed account pools without silent reuse
Some systems do not permit dynamic account creation. In that case, maintain a pool with at least one account per concurrent slot and select by parallelIndex. Validate pool size before tests begin and fail with a clear configuration error if it is too small.
Across shards, partition the pool explicitly or use a lease service with atomic acquisition. Environment variables containing comma-separated credentials are fragile when two jobs independently choose slot zero. A central lease should record owner, expiry, run identity, and release state so abandoned accounts can be recovered safely.
Never print credentials in a diagnostic attachment that includes workerIndex. The process identifier helps correlate logs, but secrets still require masking and scoped storage. Prefer a non-sensitive account alias for reporting.
Reserve ports and filesystem paths by slot
A worker-owned local server can derive a port from a reserved base plus parallelIndex, provided the range is dedicated and one Playwright process runs on the host. Check availability and fail clearly when another service occupies the port. On shared CI hosts or nested invocations, ask the operating system for an available port and pass it through the fixture instead of assuming global uniqueness.
Download and temporary directories should include run, project, and slot identity. Keep evidence for individual attempts in subdirectories based on workerIndex or retry, but do not let a replacement worker append to an unknown partially written file. Create directories atomically and clean only the exact namespace owned by the run.
Path cleanup must be constrained. A malformed empty run identifier should never broaden a deletion target. Validate all namespace segments before any destructive operation and reject names that do not match the framework's format.
Design for worker replacement and abrupt termination
After a failure, Playwright discards the worker and starts another process. Worker fixture teardown normally gets an opportunity to run, but infrastructure can still be interrupted by CI cancellation, machine failure, or a killed process. External resources need recovery beyond in-process finally blocks.
Make provisioning idempotent: if the schema exists with the same run owner, reset it; if another active owner holds it, fail instead of taking it. Add expiration to remote leases. Run a narrowly scoped janitor outside the test process for abandoned namespaces, using age and owner metadata rather than name patterns alone.
The replacement worker's new workerIndex is useful evidence. Attach it with the stable parallelIndex so failure analysis can show that the second attempt used a fresh process but the same resource slot.
Diagnose allocation failures systematically
If tests fail only in parallel, log non-secret namespace dimensions and look for duplicate resource ownership. Two different shards with the same run and slot but no shard dimension reveal the collision immediately. If a retry fails during provisioning, inspect whether the previous worker left a lease that teardown could not release.
An out-of-range account lookup often means code used workerIndex against a fixed pool. A resource that changes unexpectedly on retry may mean code included the process identity where a stable slot was required. Conversely, logs from multiple process lifetimes merged under one label indicate that parallelIndex was used where workerIndex should distinguish evidence.
If one slot is consistently slower, compare its assigned resource health rather than blaming test distribution. A damaged database, throttled account, or occupied port can make every test routed to that slot appear flaky.
Weigh reuse against isolation
Worker-scoped resources reduce repeated setup, but a longer reuse window increases contamination risk and makes failures affect more tests. Test-scoped resources offer stronger isolation at a higher provisioning cost. Choose scope per resource: process or container startup may be worker-scoped, while user records and mutable documents remain test-scoped.
Do not optimize fixture scope from intuition. Observe setup cost, cleanup reliability, and failure blast radius. A worker database with deterministic per-test reset is often a useful middle ground; one shared environment for all workers usually is not.
Operational resource-allocation checklist
- Use
parallelIndexfor a bounded reusable slot. - Use
workerIndexfor one process lifetime and its diagnostics. - Include run, shard, and project identity for shared external systems.
- Validate pool capacity before executing tests.
- Provision expensive infrastructure in worker-scoped fixtures.
- Reset mutable scenario state at test scope.
- Make setup idempotent and cleanup narrowly targeted.
- Handle retries as a new worker on the same logical slot.
- Mask credentials and attach only non-sensitive resource aliases.
- Recover abandoned remote leases outside the worker process.
Implement deterministic allocation in stages
First inventory every shared account, database, port, queue, and directory used by parallel tests. Assign each resource an owner scope and decide whether it follows the stable slot or the process lifetime. Move one high-collision resource behind a worker fixture, compose a complete namespace, and inject a controlled failure to observe teardown and replacement. Then add per-test reset and abandoned-resource recovery. This lifecycle-focused design makes parallel execution predictable even when workers fail, restart, and reuse the same capacity.
// LIVE COURSE / THE TESTING ACADEMY
Playwright Automation Mastery
Go beyond Selenium. Master Playwright with JS/TS in 90 days.
From the instructor behind this guide.
Playwright jobs are growing 8x faster than Selenium. 90 days / 75+ live hrs / Tue-Thu-Sat 7 AM IST.
PRIMARY REFERENCES
Verify the details at the source
QABattle guides are practical explanations. Product behavior, standards, and APIs can change, so use these primary references for the canonical details.
- 01Playwright documentation
Microsoft
Canonical API, locator, fixture, browser, and test-runner behavior.
- 02Playwright best practices
Microsoft
Official guidance for resilient tests, isolation, and user-facing locators.
- 03
FAQ / QUICK ANSWERS
Questions testers ask
What is the difference between workerIndex and parallelIndex in Playwright?
workerIndex uniquely identifies a worker process and changes when a failed worker is replaced. parallelIndex identifies a concurrent slot from zero to workers minus one and remains the same for the replacement process.
Which index should select a fixed database from a resource pool?
Use parallelIndex for a fixed slot because it remains bounded by worker count and survives process replacement. Add shard or run identity when different CI machines share the same external pool.
When is workerIndex useful?
Use workerIndex for logs, attachment names, and diagnostics that should identify one concrete process lifetime. Do not use it as a durable array offset or reusable account identity.
Should database provisioning use a worker-scoped fixture?
Usually yes when one isolated database can safely serve multiple test files in the worker. The fixture should provision once, reset between tests as needed, and release the resource during worker teardown.
How do retries affect worker-owned resources?
A failure causes the worker to be replaced, so setup can run again for the same parallel slot under a new workerIndex. Provisioning and cleanup must tolerate partial prior state and make the slot ready before reuse.
RELATED GUIDES
Continue the learning route
GUIDE 01
Build a Worker-Scoped Account Pool for Parallel Playwright Tests
Build a worker-scoped Playwright account pool with atomic leases, stable parallel identity, isolated browser contexts, and resilient cleanup.
GUIDE 02
Composing Modular Playwright Fixtures with mergeTests and Boxed Steps
Compose modular Playwright fixtures with mergeTests, explicit dependency ownership, boxed reporting, and a stable test-support import surface.
GUIDE 03
Shard Playwright Tests and Merge Reports Across CI Jobs
Split Playwright tests across balanced CI shards, preserve blob artifacts, and merge every job into one trustworthy HTML report with attachments.
GUIDE 04
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.