---
title: Run Playwright in CI with GitHub Actions | TestingBot
description: Tests that only run on a developer&#39;s laptop protect nobody. This
  chapter turns the suite into a check that runs on every push and pull request, keeps
  the…
source_url:
  html: https://testingbot.com/resources/courses/playwright/github-actions-ci
  md: https://testingbot.com/resources/courses/playwright/github-actions-ci.md
---

Course

[Playwright Tutorial: Zero to CI](https://testingbot.com/resources/courses/playwright)

Chapter 8 of 10

1. [1 Playwright Tutorial for Beginners](https://testingbot.com/resources/courses/playwright)
2. [2 Install Playwright and Run a First Test](https://testingbot.com/resources/courses/playwright/install-and-first-test)
3. [3 Locators: Find Elements the Way Users Do](https://testingbot.com/resources/courses/playwright/locators)
4. [4 Auto-Waiting and Web-First Assertions](https://testingbot.com/resources/courses/playwright/auto-waiting-and-assertions)
5. [5 Fixtures, Hooks and Logging In Once](https://testingbot.com/resources/courses/playwright/fixtures-hooks-and-authentication)
6. [6 Debugging: UI Mode, Trace Viewer, Codegen](https://testingbot.com/resources/courses/playwright/debugging-tests)
7. [7 Multiple Browsers, Workers and Sharding](https://testingbot.com/resources/courses/playwright/browsers-parallelism-and-sharding)
8. [8 Run Playwright in CI with GitHub Actions](https://testingbot.com/resources/courses/playwright/github-actions-ci)
9. [9 Run Your Playwright Suite on a Cloud Grid](https://testingbot.com/resources/courses/playwright/run-on-a-cloud-grid)
10. [10 Knowledge check](https://testingbot.com/resources/courses/playwright/knowledge-check)

[Exit course](https://testingbot.com/resources/courses)

 Chapter 8 of 10 Beginner 8 min read 3 quick checks Updated Nov 2026 

# Run Playwright in CI with GitHub Actions
On this page
1. [The generated workflow](https://testingbot.com#the-generated-workflow)
2. [Reading a failure from CI](https://testingbot.com#reading-a-failure-from-ci)
3. [Secrets and environment](https://testingbot.com#secrets-and-environment)
4. [Faster installs with the Playwright container](https://testingbot.com#faster-installs-with-the-playwright-container)
5. [Sharding across jobs](https://testingbot.com#sharding-across-jobs)
6. [Other CI systems](https://testingbot.com#other-ci-systems)
7. [When CI browsers are not enough](https://testingbot.com#when-ci-browsers-are-not-enough)

Tests that only run on a developer's laptop protect nobody. This chapter turns the suite into a check that runs on every push and pull request, keeps the report and traces when something fails, and then scales out with sharding.

## The generated workflow[#](https://testingbot.com#the-generated-workflow)

If you answered "yes" to the GitHub Actions question in chapter 1, you already have `.github/workflows/playwright.yml`. This is what `npm init playwright@latest` generates with Playwright 1.65:

```yaml
name: Playwright Tests
on:
  push:
    branches: [main, master]
  pull_request:
    branches: [main, master]
jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: lts/*
    - name: Install dependencies
      run: npm ci
    - name: Install Playwright Browsers
      run: npx playwright install --with-deps
    - name: Run Playwright tests
      run: npx playwright test
    - uses: actions/upload-artifact@v4
      if: ${{ !cancelled() }}
      with:
        name: playwright-report
        path: playwright-report/
        retention-days: 30
```

Step by step:

- **Triggers** : every push to, and every pull request into, `main` or `master`. Adjust the branch names to your repository.
- **`npm ci`** installs exactly what `package-lock.json` says. Commit the lock file, or this step fails.
- **`npx playwright install --with-deps`** downloads the browser builds that match the installed `@playwright/test` version, plus the Linux system libraries they need. Because the version comes from your lock file, CI always gets the browsers your tests were written against.
- **`npx playwright test`** runs the suite. GitHub Actions sets `CI=true`, so the config's CI branches kick in: `forbidOnly` fails the run on a stray `test.only`, failed tests get two retries, and traces are recorded on the first retry.
- **`upload-artifact`** with `if: ${{ !cancelled() }}` uploads the HTML report even when tests failed, which is exactly when you need it. Skipping only on cancellation avoids uploading half-written reports.

The generator pins `v4` of the official actions. Newer major versions exist; upgrading them (for example via Dependabot) is fine, but it is not required for Playwright to work.

The Playwright CI docs suggest one more change: instead of relying on the job-level `timeout-minutes`, set a `globalTimeout` in `playwright.config.ts`. When a GitHub job times out it is killed and you get no report; when Playwright's own global timeout fires, the reporters still write their output.

```typescript
export default defineConfig({
  // Fail the run after an hour, so reporters still produce a report
  globalTimeout: 60 * 60 * 1000,
});
```

A slow run hit the job's `timeout-minutes` and there is no HTML report at all. What change keeps a report next time?

 Raise `timeout-minutes` to 120 Set a `globalTimeout` in the Playwright config below the job timeout Change the upload step to `if: always()` Set `retries: 0` on CI

A job killed by GitHub never lets the reporters finish. When Playwright's own global timeout fires first, the run ends cleanly and the report is written and uploaded.

## Reading a failure from CI[#](https://testingbot.com#reading-a-failure-from-ci)

When the job goes red:

1. Open the workflow run and download the `playwright-report` artifact from the summary page.
2. Unzip it and serve it locally: `npx playwright show-report path/to/playwright-report`.
3. Open the failed test. If it was retried, its trace is attached; open it and work through it as described in chapter 5.

A test marked **flaky** passed on retry, so the job stayed green. Flaky tests are worth a look on every run; the report lists them separately.

## Secrets and environment[#](https://testingbot.com#secrets-and-environment)

Your tests read credentials and URLs from environment variables, so pass them in from GitHub secrets on the test step:

```yaml
- name: Run Playwright tests
      run: npx playwright test
      env:
        BASE_URL: ${{ vars.STAGING_URL }}
        APP_USER: ${{ secrets.APP_USER }}
        APP_PASSWORD: ${{ secrets.APP_PASSWORD }}
```

Secrets are masked in logs, but they are not masked inside traces, screenshots or videos. If a test types a password, that value can be visible in the trace's DOM snapshots and in the action log. Use dedicated test accounts with no access to real data, and keep artifact retention short for anything sensitive.

Your login test types a password read from a GitHub secret. Where can that password still show up in plain text?

 In the job's console log In the trace, screenshots and videos inside the uploaded report Nowhere: secrets are masked everywhere

GitHub masks secrets in logs, not in artifacts. Trace DOM snapshots and the action log can contain the typed value, so use dedicated test accounts and short retention.

## Faster installs with the Playwright container[#](https://testingbot.com#faster-installs-with-the-playwright-container)

Installing browsers and system dependencies on every run takes time. An alternative is to run the job inside Microsoft's Docker image, which already contains the browsers and their dependencies. The image tag must match your `@playwright/test` version exactly:

```yaml
jobs:
  test:
    runs-on: ubuntu-latest
    container:
      image: mcr.microsoft.com/playwright:v1.65.0-noble
      options: --user 1001
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: lts/*
    - run: npm ci
    - run: npx playwright test
```

When you upgrade Playwright, upgrade the image tag in the same commit. A mismatch shows up as an error about missing browser executables.

The Playwright docs also advise against caching the browser binaries between runs: restoring the cache takes about as long as downloading them, and on Linux the system dependencies cannot be cached anyway. If you only test one browser, `npx playwright install chromium --with-deps` is the cheaper speed-up.

## Sharding across jobs[#](https://testingbot.com#sharding-across-jobs)

When a single job gets too slow, split it with a matrix. Each job runs one shard and uploads a blob report; a final job merges them into one HTML report. First, switch reporters on CI in `playwright.config.ts`:

```typescript
reporter: process.env.CI ? 'blob' : 'html',
```

Then the workflow:

```yaml
name: Playwright Tests
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
jobs:
  playwright-tests:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        shardIndex: [1, 2, 3, 4]
        shardTotal: [4]
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: lts/*
    - run: npm ci
    - run: npx playwright install --with-deps
    - run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
    - uses: actions/upload-artifact@v4
      if: ${{ !cancelled() }}
      with:
        name: blob-report-${{ matrix.shardIndex }}
        path: blob-report
        retention-days: 1

  merge-reports:
    if: ${{ !cancelled() }}
    needs: [playwright-tests]
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: lts/*
    - run: npm ci
    - uses: actions/download-artifact@v4
      with:
        path: all-blob-reports
        pattern: blob-report-*
        merge-multiple: true
    - run: npx playwright merge-reports --reporter html ./all-blob-reports
    - uses: actions/upload-artifact@v4
      with:
        name: html-report--attempt-${{ github.run_attempt }}
        path: playwright-report
        retention-days: 14
```

Points that are easy to get wrong:

- `fail-fast: false` lets the other shards finish when one fails; otherwise you lose most of the results.
- The merge job uses `if: ${{ !cancelled() }}` so it runs even when shards failed, which is when the merged report matters.
- Blob artifacts are only needed until the merge, hence `retention-days: 1`.
- Leave `fullyParallel: true` on so shards are balanced at test level (chapter 6).

Your sharded matrix leaves out `fail-fast: false`. Shard 2 fails early. What do you lose?

 Nothing: the merge job still has all four blob reports Only the trace for the failing test The results of the shards GitHub cancels when shard 2 fails The ability to retry the failed test

Matrix jobs fail fast by default, so the other shards are cancelled and most of the results never exist. `fail-fast: false` lets them finish.

## Other CI systems[#](https://testingbot.com#other-ci-systems)

Nothing here is GitHub-specific apart from the YAML. On any CI provider the recipe is the same: `npm ci`, `npx playwright install --with-deps` (or the Docker image), `npx playwright test`, and keep `playwright-report/` and `test-results/` as build artifacts. The official [CI guide](https://playwright.dev/docs/ci) has examples for Azure Pipelines, CircleCI, GitLab CI and Jenkins.

## When CI browsers are not enough[#](https://testingbot.com#when-ci-browsers-are-not-enough)

The runner above tests the Linux builds of Chromium, Firefox and WebKit on whatever machine GitHub provides. That covers a great deal, but it does not test branded Chrome or Edge on Windows, or WebKit on macOS, and parallelism is bounded by how many runners you can afford. The next chapter keeps this workflow and points the same tests at remote browsers. Our [GitHub Actions integration guide](https://testingbot.com/support/integrations/ci-cd/github-actions) covers the TestingBot-specific parts of that setup, such as storing your API key as a secret.

[Previous · Chapter 7 Multiple Browsers, Workers and Sharding](https://testingbot.com/resources/courses/playwright/browsers-parallelism-and-sharding) [Next · Chapter 9 Run Your Playwright Suite on a Cloud Grid](https://testingbot.com/resources/courses/playwright/run-on-a-cloud-grid)

On this page

1. [The generated workflow](https://testingbot.com#the-generated-workflow)
2. [Reading a failure from CI](https://testingbot.com#reading-a-failure-from-ci)
3. [Secrets and environment](https://testingbot.com#secrets-and-environment)
4. [Faster installs with the Playwright container](https://testingbot.com#faster-installs-with-the-playwright-container)
5. [Sharding across jobs](https://testingbot.com#sharding-across-jobs)
6. [Other CI systems](https://testingbot.com#other-ci-systems)
7. [When CI browsers are not enough](https://testingbot.com#when-ci-browsers-are-not-enough)

Certification

This course has a free, hands-on certificate.

[How it works](https://testingbot.com/resources/courses/playwright/certification)
