Automated speed tests and performance budgets

Catch a slowdown the day it is made. What a performance budget is, how Lighthouse CI works, why one run misleads, and a routine for teams without a build.

AdvancedHow-to7 min readUpdated October 30, 2026

In this guide
  1. Why automate
  2. What a performance budget is
    1. Two levels
    2. Where the numbers come from
  3. Why one run is not enough
  4. Lighthouse CI
    1. The steps
    2. Assertions
    3. An example
  5. The limits of a build-time test
  6. If you have no build process
  7. When a check fails
  8. Mistakes to avoid
  9. Check your own page
  10. Sources

Why automate

Without a check, speed work decays. A fix is made, the scores go up, and over the next months new content, plugins and scripts pull them down again. Nobody notices until a customer or a search report does.

An automated test makes speed a property of every change. It tells you which change cost what, while the change is fresh and small enough to fix.

What a performance budget is

MDN defines a performance budget as a limit to prevent regressions. It can apply to a file, a file type, all the files on a page, a specific metric, a custom metric, or a threshold over a period of time. MDN groups the goals into three kinds:

The primary goal is to prevent regressions, though budgets can also show trends and expose needs, such as a large library that has smaller alternatives.

Two levels

MDN says a budget should have two levels:

Where the numbers come from

Start from your visitors. MDN's advice is to measure the devices and connection speeds your users have, with several tools, and to turn those time-based goals into file-size budgets. It also says budgets should be dynamic: you may accept a feature that costs load time if it is meant to raise engagement.

For a first set, the Core Web Vitals thresholds are a natural error level: 2.5 seconds for Largest Contentful Paint, 200 milliseconds for Interaction to Next Paint, 0.1 for Cumulative Layout Shift. Put a tighter figure on the warning level. For sizes, look at the current page and set the budget at or just below it, so that it only moves in the right direction. See page weight and requests.

File-size checks in a build, such as the performance warnings in a bundler, are a first line of defense. MDN adds a caution: size is hard to translate into time, because a development build may lack the third-party scripts and the CDN optimizations of the live site.

Why one run is not enough

Lighthouse's own variability documentation starts with this: scores change because of variability in web and network technology even if the code has not changed, so run it multiple times and be wary of variability before concluding that a change made a difference.

It lists the sources. The biggest are page nondeterminism (for example, an A/B test or different ads), local network variability, client hardware variability and resource contention from other programs on the machine. Its simulated throttling reduces some of these, and nothing removes page nondeterminism. If the page differs between runs, the numbers will too, so test the exact same version of the page.

Its advice for stable results in an automated setting:

Lighthouse CI runs three times per URL by default, and you can raise it with the numberOfRuns setting.

Lighthouse CI

Lighthouse CI is the Lighthouse team's tool for running Lighthouse in a build. According to its documentation, after setup your build system runs Lighthouse on your project's URLs on every commit, asserts that chosen audits pass, and uploads the reports for inspection.

The steps

  1. Prerequisites. Your code is in git, your changes go through a continuous integration system such as GitHub Actions, the build can produce production assets, and you have either a command that serves them or a static site.
  2. Collect. A file named lighthouserc.js at the root of the repository holds the settings. The command lhci autorun runs the sequence. It finds the static files, or starts your server, and runs Lighthouse on each page.
  3. Upload. Reports can go to "temporary public storage" or to your own Lighthouse CI server. The documentation warns that the temporary storage is public: anyone with the URL can see the reports. Read its terms first, or use your own server.
  4. Assert. Add rules, and the build fails or warns when a rule is broken.

The documentation recommends starting slowly. Collect results first. Add assertions later, once the scores are a little higher.

Assertions

An assertion names a Lighthouse audit and a level in the style of a code linter: off, warn or error. You can check the score, the number of items or the numeric value, with minScore, maxLength and maxNumericValue. A preset such as lighthouse:recommended starts you with the Lighthouse team's list of audits.

A practical approach from the documentation: start with the preset, look at the failures in the build log, switch those audits off, and treat that list as a burndown list of issues to fix.

For several runs, an aggregationMethod decides which value counts: the most favorable, the least favorable, or the run judged most representative. For different pages with different limits, assertMatrix applies different assertions to different URL patterns.

An example

This configuration is an example for this guide, not a recommendation of particular numbers:

module.exports = {
  ci: {
    collect: {
      url: ['https://staging.example.com/', 'https://staging.example.com/pricing/'],
      numberOfRuns: 5,
    },
    assert: {
      assertions: {
        'categories:performance': ['warn', {minScore: 0.9}],
        'largest-contentful-paint': ['error', {maxNumericValue: 2500}],
        'cumulative-layout-shift': ['error', {maxNumericValue: 0.1}],
        'total-blocking-time': ['warn', {maxNumericValue: 200}],
      },
    },
    upload: {target: 'temporary-public-storage'},
  },
};

Use a staging or preview address, and the same settings every time.

The limits of a build-time test

If you have no build process

A site on a hosted builder or a WordPress site updated through a dashboard has no build step to hook into. The same discipline works by hand or on a schedule:

  1. Pick five pages: home, two landing pages, one content page, one form or cart page.
  2. Test each with the same tool and settings once a month, and after every plugin, theme or script change. Take the median of several runs. See why scores change.
  3. Write the numbers and the date in a sheet, with a note of what changed.
  4. Set a rule, for example that Largest Contentful Paint over 2.5 seconds on any of the five pages means someone looks into it this week.
  5. Check the real-visitor data in PageSpeed Insights each month. See the Chrome UX Report.

Online monitoring services that run tests on a schedule and send alerts also exist. Compare what they measure and how often before you rely on one.

When a check fails

  1. Run it again, and check that the page and the settings are the same.
  2. Compare with the last good run: which change went in between?
  3. Open the report and find the audit that changed.
  4. Fix it, or decide on purpose to accept it and change the budget, with a note of why.

Mistakes to avoid

Check your own page

Run the free audit on your five key pages and record the results. That is your baseline. Set a warning level a little above each result and an error level at the Core Web Vitals threshold, and repeat the test on a schedule.

Sources

Start here

Free website audit

Live Google data for any public page: mobile and desktop.

Mobile + desktop · Lighthouse lab test · Real-user Chrome data when Google has it

Rather have this done for you? Core Web Vitals · Book a call

Related guides

All guides →