Introduction
Running Playwright tests locally is one thing; running them reliably in CI/CD is where most teams hit friction. Playwright in GitHub Actions: what developers should know covers the practical setup that keeps test suites stable, fast, and debuggable across every pull request without surprises.
We've seen teams waste weeks troubleshooting flaky tests in CI only to discover they hadn't installed browser dependencies correctly, or they were pulling stale artifacts. This article walks through the real patterns that work in production—from browser installation to artifact handling to using Docker images that eliminate environment drift.
Why CI matters for Playwright
Running tests only on your machine is a false sense of security. Your OS, Node version, browser cache, and system libraries are all different from CI. When a test passes locally but fails in GitHub Actions, you lose developer time diagnosing environment issues instead of fixing actual bugs.
The goal of a solid CI setup is reproducibility: every pull request gets the exact same test environment, the same browser versions, and the same dependencies. This removes the "works on my machine" excuse and catches real regressions early.
Step 1: Install browsers with dependencies
The first—and most common—mistake is assuming Playwright's browser binaries are enough. They're not. Playwright bundles Chromium, Firefox, and WebKit, but those binaries need system libraries to run. On Ubuntu (common in CI), that means fonts, graphics libraries, and audio dependencies.
The command is simple but essential:
npx playwright install --with-depsThe --with-deps flag installs not just the browsers but also system packages required to run them. Without it, you'll see cryptic errors like "error while loading shared libraries" when Playwright tries to spawn a browser.
In your GitHub Actions workflow, add this early:
- name: Install Playwright browsers
run: npx playwright install --with-depsThis step should run after checking out code and installing Node dependencies (via npm ci), but before any test execution. It's fast—usually under 30 seconds on a fresh runner—and it prevents 80% of CI browser issues we've seen.
Step 2: Start your application and run tests
Most Playwright suites need your application running. Whether it's a Next.js dev server, a Docker container, or a deployed staging environment, your tests need a target.
For a typical setup, you'll run the app in the background, wait for it to be ready, then kick off Playwright:
- name: Start application
run: npm run dev &
- name: Wait for app to be ready
run: npx wait-on http://localhost:3000 --timeout 30000
- name: Run Playwright tests
run: npx playwright testBreaking this down:
- Start app in background: The
&lets the process run without blocking subsequent steps. - Wait for readiness: Tools like
wait-onpoll your app's health endpoint until it responds. This prevents race conditions where tests start before the app is actually listening. - Run tests: Once the app is ready, Playwright can connect and run your suite.
If you're using Docker Compose or have a more complex setup, start those services earlier in the workflow and point Playwright at them.
Step 3: Upload reports and artifacts
Test failures in CI are useless without evidence. Playwright generates three types of artifacts worth preserving:
- HTML report: Interactive report showing test results, timestamps, and pass/fail details.
- Traces: Detailed execution traces (with video, screenshots, network logs) for failed tests.
- Screenshots: Captured at specific test steps or on failure.
GitHub Actions' artifact system keeps these for 90 days by default. Use the upload-artifact action to capture them:
- name: Upload Playwright report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report
retention-days: 30Key points:
if: always()ensures artifacts upload even if tests fail. Without this, you lose traces when you need them most.path: playwright-reportpoints to Playwright's default report output directory.retention-dayssaves storage; 30 days is usually enough for debugging.
After the workflow completes, the "Artifacts" section in the GitHub Actions UI lets you download the report. You can then open it locally in a browser and click through failed tests to see exactly what went wrong—network requests, DOM state, video scrubs, everything.
Step 4: Use the Playwright Docker image
GitHub Actions runners come with various configurations. To eliminate environment drift, use the official Playwright Docker image: mcr.microsoft.com/playwright. This image bakes in all system dependencies, Node, and Playwright, so your tests run in a guaranteed environment.
Configure your workflow to use it:
jobs:
test:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.45.0-jammy
options: --shm-size=1gb
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Run Playwright tests
run: npx playwright testWhen you specify a container, GitHub Actions runs all steps inside that image. Benefits:
- Consistency: Every developer and CI run uses the exact same OS, libraries, and versions.
- Reproducibility: Issues tied to system configuration vanish.
- Pinned versions: You control which Playwright Docker tag you use; no surprises from auto-updates.
The --shm-size=1gb flag increases shared memory, which Chromium needs for stable rendering. Without it, you may see "Discarding image" warnings or hangs.
Note: if you're inside a container, you no longer need npx playwright install --with-deps—browsers are already there. Just run your tests.
A complete workflow example
Here's a realistic GitHub Actions workflow combining all these patterns:
name: Playwright Tests
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.45.0-jammy
options: --shm-size=1gb
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Start application
run: npm run dev &
- name: Wait for app
run: npx wait-on http://localhost:3000 --timeout 30000
- name: Run Playwright tests
run: npx playwright test
- name: Upload report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report
retention-days: 30This workflow runs on every PR and push to main. It uses the Playwright Docker image, installs your app's dependencies, starts the dev server, waits for it to respond, runs all tests, and uploads the report regardless of pass/fail.
Common mistakes
- Forgetting
--with-deps:npx playwright installwithout the flag installs browsers but not system libraries. Tests fail with cryptic shared library errors. Always use--with-depson bare Ubuntu runners. - Not using
if: always()on artifact upload: If tests fail, the step is skipped by default. You lose the report when you need it most. Always include this condition. - No wait logic before tests: Starting an app and immediately running tests causes race conditions. The app may not be fully initialized. Use
wait-onor similar health checks. - Ignoring Docker image SHM limits: Chromium renders to shared memory. Low SHM causes hangs and timeouts. Set
--shm-size=1gbin the container options. - Running tests on every file change: Consider using job conditions to skip tests for documentation-only changes. This saves CI minutes and keeps feedback fast.
Optimization tips
Once your basic setup works, consider these refinements:
- Cache Node modules: Use
actions/setup-node@v4withcache: npmto skip reinstalling dependencies each run. - Parallel execution: Playwright runs tests in parallel by default. Set
workersin yourplaywright.config.tsto match CI runner capacity. - Conditional test runs: Use GitHub Actions path filters to skip tests if only docs changed.
- Report merging: If you split tests across multiple jobs, merge reports with
playwright merge-reports.
Key takeaways
- Always use
npx playwright install --with-depson standard CI runners to install both browser binaries and required system libraries. - Start your application, wait for it to be ready, then run tests—race conditions are subtle but common.
- Upload Playwright artifacts with
if: always()to capture reports and traces even when tests fail. - Use the official Playwright Docker image (
mcr.microsoft.com/playwright) to eliminate environment drift and guarantee consistent test execution. - Invest time in CI setup early; flaky or opaque CI wastes developer time and undermines test value.






Comments
Loading comments…