GitHub Actions is the most common CI for Playwright. Free for public repos. 2,000 minutes/month free for private repos. And Playwright has first-class support for it. Let us set it up from scratch.
Create this file in your repo. This is a production-ready workflow that handles everything -- install, run, report, and artifact upload.
name: Playwright Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
- name: Upload HTML report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 14
- name: Upload test traces
uses: actions/upload-artifact@v4
if: failure()
with:
name: test-traces
path: test-results/
retention-days: 7actions/checkout@v4 -- clones your repo into the CI runner
actions/setup-node@v4 -- installs Node.js 20, caches npm packages for speed
npm ci -- installs exact versions from package-lock.json (not npm install)
npx playwright install --with-deps -- downloads browsers AND system dependencies (libgbm, libnss, etc.)
npx playwright test -- runs all tests in headless mode
Upload HTML report -- always uploads, even if tests fail (if: always())
Upload traces -- only uploads on failure to save storage (if: failure())
Always use npm ci instead of npm install in CI. npm ci uses exact versions from package-lock.json and is faster. npm install might update versions and cause inconsistencies.
Want to run tests every night at 2 AM? Add a cron schedule. This catches flaky tests and environment issues before the team starts work.
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
# Run every day at 2 AM UTC
- cron: '0 2 * * *'Browser download takes 1-2 minutes. Cache them to speed up subsequent runs.
- name: Get Playwright version
id: playwright-version
run: echo "version=$(npx playwright --version)" >> $GITHUB_OUTPUT
- name: Cache Playwright browsers
uses: actions/cache@v4
id: playwright-cache
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }}
- name: Install Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: npx playwright install --with-depsThe cache key includes the Playwright version. When you upgrade Playwright, the cache invalidates automatically and downloads new browsers. Old cache is cleaned up by GitHub after 7 days of no access.
Q: How do you set up Playwright tests in GitHub Actions?
A: Create a workflow file at .github/workflows/playwright.yml. The key steps are: checkout code, setup Node.js, run npm ci for dependencies, run npx playwright install --with-deps to get browsers and system libraries, then npx playwright test. Upload HTML report as artifact with if: always() so it is available even on failure. Upload trace files with if: failure() for debugging. Use npm ci, not npm install, for deterministic builds.
Key Point: The --with-deps flag is critical. Without it, Playwright browsers install but system dependencies like libgbm and libnss are missing. Tests will crash with cryptic errors.
Key Point: Always use npx playwright install --with-deps in CI. The --with-deps flag installs system libraries that browsers need to run.