Why Playwright Needs CI-Specific Configuration
Continuous integration environments differ fundamentally from a developer's laptop: they're ephemeral, resource-constrained, and headless by default, so Playwright ships CI-aware defaults—retries default to 2 on CI versus 0 locally, and the reporter switches to a more CI-friendly format instead of the interactive 'list' reporter—triggered automatically when process.env.CI is set. Because CI runners are shared and often throttled, tests that pass reliably locally can fail intermittently on CI purely from timing pressure, which is why Playwright's auto-waiting and web-first assertions matter even more in this environment.
Cricket analogy: Playing a day-night Test under lights is different from a club match at your local ground — Playwright automatically switches to CI-tuned settings like more retries, just as a team adjusts its batting order for tricky floodlit conditions.
Setting Up GitHub Actions
A typical GitHub Actions workflow checks out the repo, installs Node dependencies, runs npx playwright install --with-deps to fetch browser binaries plus their OS-level dependencies (fonts, codecs, X11 libraries), and then executes npx playwright test, uploading the playwright-report directory as a build artifact so failures can be inspected after the job completes. The --with-deps flag is essential on fresh Ubuntu runners because the Chromium, Firefox, and WebKit builds Playwright downloads need system libraries that aren't present in a minimal CI image by default.
Cricket analogy: Setting up a GitHub Actions workflow is like arranging a full match-day schedule: toss, warm-up nets, then the innings — checkout code, install dependencies, install browsers, then run the actual Playwright test innings.
name: Playwright Tests
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 14Docker Images and Browser Installation
For teams running Playwright inside Docker (e.g., Kubernetes-based CI or self-hosted runners), Microsoft publishes official mcr.microsoft.com/playwright images pinned to a specific Playwright version, pre-installed with all three browser engines and their OS dependencies, avoiding the need to run playwright install separately and guaranteeing the browser binary version always matches the npm package version. Version mismatches between the Playwright npm package and installed browsers are a common source of 'browser not found' errors, so pinning the Docker image tag to match package.json's playwright version is a standard CI hygiene practice.
Cricket analogy: Using Microsoft's pre-built Docker image with browsers baked in is like a touring team using pre-approved neutral-venue kit and stadium instead of shipping their own turf, guaranteeing everything matches without last-minute setup surprises.
FROM mcr.microsoft.com/playwright:v1.45.0-jammy
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
# Browsers and OS deps are already installed in the base image
CMD ["npx", "playwright", "test"]Always pin the Docker image tag (e.g., v1.45.0-jammy) to match the playwright version in package.json exactly. A mismatched image and npm package version is the single most common cause of cryptic 'Executable doesn't exist' errors in containerized CI.
CI-Aware Config Adjustments
Beyond retries and reporters, playwright.config.ts commonly branches on process.env.CI to disable test.only enforcement (forbidOnly: !!process.env.CI, which fails the build if a developer accidentally left a .only in committed code), reduce workers to avoid oversubscribing shared runners, and enable trace: 'on-first-retry' so traces are only captured for failing tests, keeping artifact storage costs down while still giving full debugging context when something breaks in CI but not locally.
Cricket analogy: forbidOnly on CI is like an umpire enforcing a rule during a televised final that's relaxed during a friendly net session — a stricter standard applies only when the stakes and audience are higher.
Forgetting to set forbidOnly: !!process.env.CI means a developer's accidental test.only() left in a commit will silently make CI run (and appear to pass) only that one test, hiding the fact that the rest of the suite never executed.
- Playwright auto-detects process.env.CI and adjusts defaults like retries and the reporter format.
- A standard GitHub Actions workflow: checkout, install deps, playwright install --with-deps, run tests, upload the report artifact.
- --with-deps installs OS-level libraries (fonts, codecs) that browser binaries need on minimal Linux runners.
- Official mcr.microsoft.com/playwright Docker images bundle browsers pre-installed and version-matched.
- Always pin the Docker image tag to match the playwright npm package version to avoid mismatch errors.
- forbidOnly: !!process.env.CI fails the build if a stray test.only() was left in committed code.
- trace: 'on-first-retry' balances debugging detail against artifact storage cost on CI.
Practice what you learned
1. What environment variable does Playwright check to auto-enable CI-specific defaults?
2. Why is npx playwright install --with-deps needed on a fresh Ubuntu CI runner?
3. What is the main benefit of using the official mcr.microsoft.com/playwright Docker image?
4. What does forbidOnly: !!process.env.CI protect against?
5. Why might a team set trace: 'on-first-retry' instead of trace: 'on' in CI?
Was this page helpful?
You May Also Like
Parallel Workers and Sharding
How Playwright Test runs suites faster using isolated worker processes locally and distributes them across multiple CI machines with sharding.
Retries and Flaky Test Handling
Understanding why Playwright tests flake, how to configure retries safely, and how auto-waiting fixes root causes instead of masking them.
Reporters and HTML Reports
How Playwright's built-in reporters work, how to configure and combine them, and how to build a custom reporter for bespoke workflows.