---
title: "Lighthouse CI GitHub Actions: Setup Guide & Documentation"
description: "Complete Lighthouse CI GitHub Actions documentation. Step-by-step YAML workflows, status checks, PR comments, and treosh/lighthouse-ci-action setup."
canonical_url: "https://unlighthouse.dev/learn-lighthouse/lighthouse-ci/github-actions"
last_updated: "2026-07-05T10:28:34.129Z"
---

Run Lighthouse audits on every push and pull request using GitHub Actions. This guide covers basic setup through advanced configurations with status checks and PR comments.

<note>

**Requirements**: LHCI 0.15.x requires Node 18+. GitHub Actions Ubuntu runners include Chrome pre-installed at `/usr/bin/google-chrome`.

</note>

## Basic Workflow

Create `.github/workflows/lighthouse.yml`:

```yaml
name: Lighthouse CI
on: [push]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 20 # Required for base branch comparison
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci && npm run build
      - run: npm install -g @lhci/cli@0.15.x
      - run: lhci autorun
```

<warning>

**Critical**: Always set `fetch-depth: 20` or higher. [Shallow clones break LHCI's ancestor detection](https://github.com/GoogleChrome/lighthouse-ci/blob/main/docs/troubleshooting.md) and cause "Could not find hash" errors.

</warning>

Add `lighthouserc.js` to your repo root:

```js
export default {
  ci: {
    collect: {
      staticDistDir: './dist',
    },
    upload: {
      target: 'temporary-public-storage',
    },
  },
}
```

## Testing a Live URL

If your site is already deployed:

```js
const config2 = {
  ci: {
    collect: {
      url: ['https://example.com/', 'https://example.com/about'],
    },
    upload: {
      target: 'temporary-public-storage',
    },
  },
}
```

## Testing with a Local Server

For apps that need a running server:

```js
const config3 = {
  ci: {
    collect: {
      startServerCommand: 'npm run start',
      url: ['http://localhost:3000/'],
    },
    upload: {
      target: 'temporary-public-storage',
    },
  },
}
```

LHCI starts your server, waits for it to be ready, runs audits, then shuts it down.

## GitHub Status Checks

Add Lighthouse results as [GitHub](https://github.com) status checks on PRs.

### Option 1: GitHub App (Recommended)

1. Install the [Lighthouse CI GitHub App](https://github.com/apps/lighthouse-ci)
2. Authorize it for your repository
3. Copy the token from the authorization page
4. Add it as a repository secret named `LHCI_GITHUB_APP_TOKEN`

<note type="info">

**Why GitHub App over PAT?** [GitHub App tokens expire in 8 hours](https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/deciding-when-to-build-a-github-app) vs PATs which can be indefinite. Apps also use fine-grained permissions scoped to status checks only, making them more secure for public repos.

</note>

Update your workflow:

```yaml
name: Lighthouse CI
on: [push, pull_request]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci && npm run build
      - run: npm install -g @lhci/cli@0.15.x
      - run: lhci autorun
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
```

The `ref: ${{ github.event.pull_request.head.sha }}` checks out the correct commit for PRs.

### Option 2: Personal Access Token

1. Create a [personal access token](https://github.com/settings/tokens/new) with `repo:status` scope
2. Add it as a secret named `LHCI_GITHUB_TOKEN`

```yaml
- run: lhci autorun
  env:
    LHCI_GITHUB_TOKEN: ${{ secrets.LHCI_GITHUB_TOKEN }}
```

## Adding Assertions

Fail the build if performance drops below thresholds:

```js
const config4 = {
  ci: {
    collect: {
      staticDistDir: './dist',
    },
    assert: {
      preset: 'lighthouse:recommended',
    },
    upload: {
      target: 'temporary-public-storage',
    },
  },
}
```

Or with custom thresholds:

```js
const config5 = {
  ci: {
    collect: {
      staticDistDir: './dist',
    },
    assert: {
      assertions: {
        'categories:performance': ['error', { minScore: 0.9 }],
        'categories:accessibility': ['error', { minScore: 0.9 }],
        'first-contentful-paint': ['warn', { maxNumericValue: 2000 }],
      },
    },
    upload: {
      target: 'temporary-public-storage',
    },
  },
}
```

## Using treosh/lighthouse-ci-action

The community built the [lighthouse-ci-action](https://github.com/treosh/lighthouse-ci-action) (1.2k+ stars) in collaboration with the Lighthouse team to simplify the workflow:

```yaml
name: Lighthouse CI
on: [push]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Lighthouse
        uses: treosh/lighthouse-ci-action@v12
        with:
          urls: |
            https://example.com/
            https://example.com/about
          budgetPath: ./budget.json
          uploadArtifacts: true
```

With assertions:

```yaml
- uses: treosh/lighthouse-ci-action@v12
  with:
    urls: https://example.com/
    configPath: ./lighthouserc.js
```

## Testing Preview Deploys

For [Vercel](https://vercel.com), [Netlify](https://netlify.com), or Cloudflare Pages preview deployments:

```yaml
name: Lighthouse on Preview
on: deployment_status

jobs:
  lighthouse:
    if: github.event.deployment_status.state == 'success'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm install -g @lhci/cli@0.15.x
      - run: |
          lhci autorun \
            --collect.url=${{ github.event.deployment_status.target_url }} \
            --upload.target=temporary-public-storage
```

## Multiple URLs with Matrix

Test multiple pages in parallel:

```yaml
name: Lighthouse CI
on: [push]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        url:
          - https://example.com/
          - https://example.com/pricing
          - https://example.com/docs
    steps:
      - uses: actions/checkout@v4
      - run: npm install -g @lhci/cli@0.15.x
      - run: |
          lhci autorun \
            --collect.url=${{ matrix.url }} \
            --upload.target=temporary-public-storage
```

## Uploading Reports as Artifacts

Save HTML reports for later review:

```yaml
- run: lhci autorun
- uses: actions/upload-artifact@v4
  if: always()
  with:
    name: lighthouse-report
    path: .lighthouseci/
    retention-days: 14
```

## Running Multiple Times

Reduce variance by running multiple audits per URL:

```js
const config6 = {
  ci: {
    collect: {
      numberOfRuns: 5,
      url: ['https://example.com/'],
    },
    upload: {
      target: 'temporary-public-storage',
    },
  },
}
```

LHCI uses the median result for assertions. [Google's research](https://developers.google.com/web/tools/lighthouse/variability) shows the median of 5 runs is **twice as stable** as a single run. Even 3 runs [reduces variance by 37%](https://developers.google.com/web/tools/lighthouse/variability).

## Caching Chrome

Speed up workflows by caching Puppeteer's Chrome:

```yaml
- uses: actions/cache@v4
  with:
    path: ~/.cache/puppeteer
    key: ${{ runner.os }}-puppeteer-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-puppeteer-
```

## Token Security

The `LHCI_TOKEN` (build token for LHCI Server) is [write-only and additive](https://googlechrome.github.io/lighthouse-ci/docs/troubleshooting.html) - it cannot read or destroy historical data. This makes it safe to use in public repos as a secret without risk of data exfiltration.

## Full Production Workflow

Complete example with all features:

```yaml
name: Lighthouse CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}
          fetch-depth: 20 # Required for ancestor comparison

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

      - run: npm ci
      - run: npm run build

      - run: npm install -g @lhci/cli@0.15.x

      - run: lhci autorun
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}

      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: lighthouse-report
          path: .lighthouseci/
          retention-days: 14
```

With `lighthouserc.js`:

```js
const config7 = {
  ci: {
    collect: {
      staticDistDir: './dist',
      numberOfRuns: 3,
    },
    assert: {
      preset: 'lighthouse:recommended',
      assertions: {
        'categories:performance': ['error', { minScore: 0.8 }],
        'categories:accessibility': ['error', { minScore: 0.9 }],
      },
    },
    upload: {
      target: 'temporary-public-storage',
    },
  },
}
```

## Next Steps

- [Configuration Reference](/learn-lighthouse/lighthouse-ci/configuration) - All lighthouserc.js options
- [Performance Budgets](/learn-lighthouse/lighthouse-ci/budgets) - Set up assertions and budgets
- [Troubleshooting](/learn-lighthouse/lighthouse-ci/troubleshooting) - Fix common issues

## When Budgets Fail

If your assertions fail, use these guides to fix the basic issues:

- [Fix LCP](/learn-lighthouse/lcp) - Largest Contentful Paint optimization
- [Fix CLS](/learn-lighthouse/cls) - Cumulative Layout Shift fixes
- [Fix INP](/learn-lighthouse/inp) - Interaction to Next Paint improvements
- [Core Web Vitals Overview](/learn-lighthouse/core-web-vitals) - Understanding all metrics
