Skip to main content
PlaywrightCI/CDtest automation

Playwright Tests Fail in CI but Pass Locally: Fix the Environment Drift

12 September 2026 · OpenCrevo

Playwright tests fail in CI but pass locally for one reason: the job is not the same machine you just used. Headless Chromium, a missing system dependency, UTC instead of your laptop timezone, or a different baseURL will fail a locator that looked fine on headed Chrome. This is environment drift, not a mysterious flake. Fix it by making CI replayable, then decide if the remaining failures belong in the flaky Playwright post (/blog/reduce-flaky-playwright-tests-ci-cd/).

The failure you are actually looking at

A checkout spec is green on your Mac. The same commit goes red on GitHub Actions: TimeoutError waiting for getByRole('button', { name: 'Pay now' }). You rerun locally. Green again. The product did not change. The browser, clock, and URL did.

  • Missing browsers. Local Playwright cache exists; CI never ran npx playwright install --with-deps.
  • Timezone and locale. Local en-GB / Europe/London; CI UTC. Dates and currency strings shift.
  • baseURL mismatch. Localhost:3000 vs a preview host that still returns the marketing shell.
  • Headed vs headless. A hover or animation that only completes when a window is visible.

How to fix Playwright tests that fail in CI but pass locally

Do not add retries yet. First make the CI job identical to a documented local command, then capture a trace on the first failure.

name: e2e
on: [pull_request]
jobs:
  playwright:
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npx playwright install --with-deps chromium
      - name: Run Playwright
        env:
          TZ: UTC
          LANG: en_US.UTF-8
          CI: true
          BASE_URL: ${{ vars.BASE_URL }}
        run: npx playwright test --project=chromium --reporter=github
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: playwright-trace
          path: test-results/
import { defineConfig } from "@playwright/test";

export default defineConfig({
  use: {
    baseURL: process.env.BASE_URL ?? "http://127.0.0.1:3000",
    locale: "en-US",
    timezoneId: "UTC",
    trace: "on-first-retry",
    screenshot: "only-on-failure",
    video: "retain-on-failure",
  },
  retries: process.env.CI ? 1 : 0,
  workers: process.env.CI ? 2 : undefined,
});
  • Run the same install command locally: npx playwright install --with-deps chromium.
  • Set TZ=UTC and BASE_URL before npm test so your laptop matches the job.
  • Open the uploaded trace with npx playwright show-trace and compare the URL bar, not just the screenshot.
  • If the trace shows a different host or an empty app shell, fix deploy/preview — not the locator.

Pass, fail, cleanup

  • Pass: The same spec fails locally when you export CI=true TZ=UTC BASE_URL=<preview>, and the CI trace URL matches that run.
  • Fail: It only fails on the hosted runner and you still cannot reproduce with those three env vars. That is still drift (OS fonts, GPU) — pin the container image next, do not raise retries.
  • Cleanup: Delete one-off sleep() calls added during the panic. Keep one retry for residual flake; treat a second retry as a smell. See the CI trust playbook.

If the suite is green locally and red in CI after the environment is locked, you need a quality engineer to instrument the pipeline, not another locator rewrite. Score your current setup with the free QA maturity assessment, then reach out through AI quality engineering services or the contact form.

START YOUR QUALITY JOURNEY

Your next chapter starts with a conversation.

Book a free quality audit. We'll review your AI system, identify the highest-risk failure modes, and map a quality roadmap tailored to your stack.