Bringing Lighthouse Audits Into Your CI Pipeline

Lighthouse is a well-known tool for evaluating web pages against a set of performance, accessibility, SEO, and PWA criteria. While you can run audits from Chrome DevTools, via a web interface, or with the Node CLI, all of these approaches require manual effort. Lighthouse CI automates this process so code changes are continuously checked against your quality budgets, and the results are visible directly in your pull requests.

Lighthouse CI works with any continuous integration provider, but this guide focuses on using it with GitHub Actions. Before integrating it into your workflow, it helps to have the tool configured and running locally.

Installing and Configuring the CLI Locally

You’ll need Node.js v10 LTS or later, as well as Google Chrome (stable), to run the Lighthouse CI command line tool. Install it globally with npm:

$ npm install -g @lhci/cli

Run lhci --help to see all available commands. The CLI reads its configuration from three places, in order of increasing precedence: a configuration file, environment variables, and CLI flags. It uses the Yargs API to handle these options. For this setup, we’ll use a configuration file.

Create a lighthouserc.js file in your project root. Your project should be tracked with Git; Lighthouse CI infers build context settings from the repository. For projects without Git, you can set these via environment variables.

The basic configuration below tells Lighthouse CI to collect reports for a static site and upload them to temporary public storage.

// lighthouserc.js
module.exports = {
  ci: {
    collect: {
      staticDistDir: './public',
    },
    upload: {
      target: 'temporary-public-storage',
    },
  },
};

Inside ci.collect, the staticDistDir option points to where your built HTML files live (for example, Hugo’s public directory or Jekyll’s _site). When Lighthouse CI runs, it starts a local server to serve those files and then shuts it down after testing.

If you use a custom server, set the startServerCommand property with the command to launch it. You’ll also need the url option to list the URLs the custom server should serve. When startServerCommand is set, Lighthouse CI runs the command and watches for the listen or ready string to confirm the server is up. If it doesn’t see this within 10 seconds, it proceeds anyway. The client then runs Lighthouse three times per URL. You can adjust the pattern to watch for via startServerReadyPattern and the timeout via startServerReadyTimeout. The number of audit rounds is controlled by numberOfRuns.

The ci.upload target determines where results go. The temporary-public-storage option uploads reports to Google Cloud Storage, where they are retained for a few days and accessible via link without authentication.

Running the Tool and Enforcing Assertions

With the configuration in place, you can trigger a run from the CLI. This will run Lighthouse three times per URL (unless changed) and upload the median result.

lhci autorun

The output will show any warnings, including a note about a GitHub token — that’s only needed later for the Actions setup. The generated link will show you the median result for each audited URL.

To make tests meaningful for your CI pipeline, you can configure the tool to fail a build when results don’t meet your standards. This is handled through the assert property.

// lighthouserc.js
module.exports = {
  ci: {
    assert: {
      preset: 'lighthouse:no-pwa',
      assertions: {
        'categories:performance': ['error', { minScore: 0.9 }],
        'categories:accessibility': ['warn', { minScore: 0.9 }],
      },
    },
  },
};

The preset option is a quick way to establish defaults. There are three options:

  • lighthouse:all: Requires a perfect score on every audit
  • lighthouse:recommended: Requires perfect scores on all non-performance audits and warns if any metric falls below 90
  • lighthouse:no-pwa: Same as lighthouse:recommended, but skips PWA audits

You can extend or override a preset with custom assertions, or skip presets and build from scratch. In the example above, the configuration enforces a baseline score of 90 for performance and accessibility. A failure on performance yields a non-zero exit code, while the accessibility failure does not. Any Lighthouse audit can be asserted, so check the documentation for available options.

You can also assert against a budget.json. This file can be created manually or generated with performancebudget.io.

// lighthouserc.js
module.exports = {
  ci: {
    collect: {
      staticDistDir: './public',
      url: ['/'],
    },
    assert: {
      budgetFile: './budget.json',
    },
    upload: {
      target: 'temporary-public-storage',
    },
  },
};

Automating Audits with GitHub Actions

Integrating Lighthouse CI into GitHub Actions requires creating a .github/workflow directory at your project root. Inside, you’ll add a YAML file (with a .yml or .yaml extension) that defines the workflow. The contents of this file will depend on your project type. The following example is one way to configure it for a Hugo-powered site.

# .github/workflow/lighthouse-ci.yaml
name: Lighthouse
on: [push]
jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
        with:
          token: ${{ secrets.PAT }}
          submodules: recursive

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2
        with:
          hugo-version: "0.76.5"
          extended: true

      - name: Build site
        run: hugo

      - name: Use Node.js 15.x
        uses: actions/setup-node@v2
        with:
          node-version: 15.x
      - name: Run the Lighthouse CI
        run: |
          npm install -g @lhci/[email protected]
          lhci autorun

This configuration creates a Lighthouse workflow with a single job named ci that runs on Ubuntu and is triggered on pushes to any branch. The job includes these steps:

  • Check out the repository. Because Hugo often uses submodules for themes, the checkout step ensures all submodules are pulled. If any submodule is in a private repo, you’ll need a GitHub Personal Access Token with repo scope. That token should be stored as a repository secret.
Silently keep paragraph text unless compacting:
  • Install Hugo on the runner using a setup action from the GitHub Actions marketplace.
  • Build the site to the public directory via the hugo command.
  • Install Node.js on the runner using the setup-node action, then install the Lighthouse CI tool.
  • Run lhci autorun.

After pushing your configuration to GitHub, the workflow will trigger. You can track its progress from the Actions tab on your repository. Even if the audit completes without errors, failed assertions will mark the run as failed. The log still contains the link to the uploaded report.

Adding Status Checks to Pull Requests

The workflow’s results are currently visible on the commit page, but you need to dig into the logs to see report links. To see the audit summary within a pull request, install the Lighthouse CI GitHub App. After installing it, copy the provided app token and add it as a repository secret named LHCI_GITHUB_APP_TOKEN.

Once installed, the status check is ready to use. You’ll see the results appear on any commit you push to a pull request at that point.

Long-Term Storage: The Lighthouse CI Server

Temporary public storage is convenient for starting out, but it’s not suitable for private or long-term report data. If that’s a requirement, look into the Lighthouse CI server. It provides a dashboard for reviewing historical Lighthouse data and comparing results between builds. To use it, you must deploy it to your own infrastructure. Instructions for deploying to Heroku and Docker are available in the project’s GitHub repository.