All posts
CI/CDLighthouseGitHub ActionsPerformance

How to Add Lighthouse to Your CI/CD Pipeline

Automate Lighthouse performance testing in GitHub Actions, GitLab CI, or CircleCI to catch regressions before they reach production.

debugutils.dev

Running Lighthouse manually before every deploy is error-prone — people forget, rush, or skip it under deadline pressure. Automating Lighthouse in CI catches performance regressions before they reach production, not after users notice the site slowed down.

The Options

Lighthouse CI (LHCI) — The official Lighthouse integration for CI systems. Open source, maintained by Google Chrome team. Runs Lighthouse, compares to a baseline, fails the build if thresholds aren’t met.

Unlighthouse — A site-wide Lighthouse scanner. Good for scanning many pages at once, but less suitable for per-commit checks.

Custom Lighthouse CLI script — Run lighthouse directly and parse the output. Most flexible, but more setup.

We’ll focus on Lighthouse CI since it’s the best maintained and most feature-complete option.

Setting Up Lighthouse CI

Install

npm install --save-dev @lhci/cli

Configure

Create .lighthouserc.js in your project root:

module.exports = {
  ci: {
    collect: {
      // URL(s) to test — use your staging URL
      url: ['http://localhost:3000/', 'http://localhost:3000/blog'],
      // Number of Lighthouse runs to average
      numberOfRuns: 3,
      // Start your dev server before collecting
      startServerCommand: 'npm run start',
    },
    assert: {
      preset: 'lighthouse:recommended',
      assertions: {
        'categories:performance': ['error', { minScore: 0.7 }],
        'first-contentful-paint': ['warn', { maxNumericValue: 2000 }],
        'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
        'total-blocking-time': ['warn', { maxNumericValue: 300 }],
        // Disable audits that don't apply
        'uses-http2': 'off',
      },
    },
    upload: {
      target: 'temporary-public-storage', // Free storage provided by LHCI team
    },
  },
};

GitHub Actions Integration

# .github/workflows/lighthouse.yml
name: Lighthouse CI

on:
  pull_request:
    branches: [main]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build

      - name: Run Lighthouse CI
        run: |
          npm install -g @lhci/cli
          lhci autorun
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}

The LHCI_GITHUB_APP_TOKEN is optional but enables Lighthouse CI to post results as a PR status check with a link to the full report.

Against a Live URL (Staging)

If you can’t start a local server in CI, test against your staging URL:

module.exports = {
  ci: {
    collect: {
      url: ['https://staging.yoursite.com/'],
      numberOfRuns: 3,
    },
    // ...
  },
};

This is simpler but requires your staging environment to be accessible.

GitLab CI Integration

# .gitlab-ci.yml
lighthouse:
  image: node:20
  stage: test
  script:
    - npm ci
    - npm run build
    - npm install -g @lhci/cli
    - lhci autorun
  only:
    - merge_requests

Handling Score Variance

Lighthouse scores vary between runs (typically ±5–10 points). To reduce noise:

  1. Set numberOfRuns: 3 (or 5 for more stability) — LHCI averages the results
  2. Set thresholds conservatively based on your performance budgets — don’t fail at 90 if your target is 75
  3. Use maxNumericValue on specific metrics rather than overall score when possible (metrics are more stable than the aggregate score)

What to Fail vs. Warn On

For a broader look at automating performance checks beyond Lighthouse, see how to automate speed testing. Not everything should fail the build:

Fail on:

Warn on:

  • Performance score below 70 (investigate but don’t block)
  • FCP over 2s
  • TBT over 500ms

Be conservative with failures — too many false positives train engineers to ignore or disable the check.

Combining CI and Manual Testing

Lighthouse CI catches regressions relative to your baseline. For deeper diagnostics — waterfall, CrUX field data, video recording — run debugutils.dev manually on your staging URL before deploying.

The combination of CI automation (to catch regressions) and manual diagnostic testing (to investigate issues) gives you the best of both worlds. For a structured manual approach, use our pre-deploy performance checklist.