Lighthouse CI with GitHub Actions: Complete Setup Guide
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.
/usr/bin/google-chrome.Basic Workflow
Create .github/workflows/lighthouse.yml:
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
fetch-depth: 20 or higher. Shallow clones break LHCI's ancestor detection and cause "Could not find hash" errors.Add lighthouserc.js to your repo root:
module.exports = {
ci: {
collect: {
staticDistDir: './dist',
},
upload: {
target: 'temporary-public-storage',
},
},
}
Testing a Live URL
If your site is already deployed:
module.exports = {
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:
module.exports = {
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 status checks on PRs.
Option 1: GitHub App (Recommended)
- Install the Lighthouse CI GitHub App
- Authorize it for your repository
- Copy the token from the authorization page
- Add it as a repository secret named
LHCI_GITHUB_APP_TOKEN
Update your workflow:
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 }} ensures the correct commit is checked out for PRs.
Option 2: Personal Access Token
- Create a personal access token with
repo:statusscope - Add it as a secret named
LHCI_GITHUB_TOKEN
- run: lhci autorun
env:
LHCI_GITHUB_TOKEN: ${{ secrets.LHCI_GITHUB_TOKEN }}
Adding Assertions
Fail the build if performance drops below thresholds:
module.exports = {
ci: {
collect: {
staticDistDir: './dist',
},
assert: {
preset: 'lighthouse:recommended',
},
upload: {
target: 'temporary-public-storage',
},
},
}
Or with custom thresholds:
module.exports = {
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 lighthouse-ci-action (1.2k+ stars) was built in collaboration with the Lighthouse team and simplifies the workflow:
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:
- uses: treosh/lighthouse-ci-action@v12
with:
urls: https://example.com/
configPath: ./lighthouserc.js
Testing Preview Deploys
For Vercel, Netlify, or Cloudflare Pages preview deployments:
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:
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:
- 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:
module.exports = {
ci: {
collect: {
numberOfRuns: 5,
url: ['https://example.com/'],
},
upload: {
target: 'temporary-public-storage',
},
},
}
LHCI uses the median result for assertions. Google's research shows the median of 5 runs is twice as stable as a single run. Even 3 runs reduces variance by 37%.
Caching Chrome
Speed up workflows by caching Puppeteer's Chrome:
- 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 — 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:
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:
module.exports = {
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 — All lighthouserc.js options
- Performance Budgets — Set up assertions and budgets
- Troubleshooting — Fix common issues
When Budgets Fail
If your assertions fail, use these guides to fix the underlying issues:
- Fix LCP — Largest Contentful Paint optimization
- Fix CLS — Cumulative Layout Shift fixes
- Fix INP — Interaction to Next Paint improvements
- Core Web Vitals Overview — Understanding all metrics
Lighthouse CI
Learn how to use @lhci/cli to automate Lighthouse audits on every commit. Prevent performance regressions, enforce budgets, and track metrics over time.
GitLab CI
Configure Lighthouse CI for GitLab pipelines. Includes .gitlab-ci.yml examples, merge request integration, and artifact storage.
- Basic Workflow
- Testing a Live URL
- Testing with a Local Server
- GitHub Status Checks
- Adding Assertions
- Using treosh/lighthouse-ci-action
- Testing Preview Deploys
- Multiple URLs with Matrix
- Uploading Reports as Artifacts
- Running Multiple Times
- Caching Chrome
- Token Security
- Full Production Workflow
- Next Steps
- When Budgets Fail