Skip to content

Performance

Lighthouse CI Performance Budgets — How Velocity X Fails the Build When LCP Slips

All articles
🚨 ⏱️ 🔴

Performance budgets catch regressions at commit time. No LCP slip makes it to production.

Here's the nightmare scenario: your site runs fine for weeks. Then a designer adds a hero image in higher resolution. A marketing team member drops a tracking script. Someone bumps a dependency. LCP creeps from 1.2s to 1.8s. Users feel it first. You find out when conversion drops. Lighthouse CI prevents that. Every commit triggers an automated Lighthouse audit. If LCP exceeds 1500ms, INP exceeds 200ms, or Performance score dips below 95, the build fails. The deploy is rejected. Your team fixes the regression *before* it ships. No surprises. No customer complaints. Just hard gates at the CI level. Velocity X uses this. A real lhci.json config, GitHub Actions integration, fail thresholds you can actually defend. Here's how.

What a Performance Budget Actually Is

A performance budget is a set of numeric limits for key metrics. If your code changes violate those limits, the build fails. It's not a nice-to-have target — it's a gate. Same logic as linting or unit tests, except the metric is "does your site load in time?"

Typical budgets for marketing/conversion sites:

  • LCP ≤ 1500ms (largest paint in viewport on mobile 4G)
  • INP ≤ 200ms (button click to visible response)
  • CLS ≤ 0.1 (layout shift; we aim for 0.0)
  • Performance score ≥ 95 (Lighthouse overall grade)

These numbers aren't arbitrary. Google's algorithm rewards them. Users bounce on slower sites. Conversions measurably drop when LCP exceeds 2.5s. Performance budgets codify the business rule: "we ship nothing slower than this."

Why "We'll Optimise Later" Fails

Without a gate, "we'll fix performance next quarter" becomes "we'll never fix it." New features land, tech debt compounds, the codebase bloats. By the time someone runs Lighthouse, the site is 3s LCP and a rewrite is cheaper than a refactor.

With Lighthouse CI, there is no "later." The regression is caught at the developer's machine before they push. They either optimise the change or revert it. Performance stays constant. No death by a thousand commits.

Lighthouse CI Setup (Real Config)

Step 1: Install and auth.

{`npm install --save-dev @lhci/cli@latest @lhci/utils

lhci wizard --github  # links your repo, creates GitHub status checks
`}

Step 2: Create lhci.json in your repo root.

{`{
  "ci": {
    "collect": {
      "url": ["https://yoursite.com/", "https://yoursite.com/pricing"],
      "numberOfRuns": 3,
      "settings": {
        "configPath": "./lighthouserc.json"
      }
    },
    "assert": {
      "preset": "lighthouse:recommended",
      "assertions": {
        "categories:performance": ["error", { "minScore": 0.95 }],
        "metrics:largest-contentful-paint": ["error", { "maxNumericValue": 1500 }],
        "metrics:interaction-to-next-paint": ["error", { "maxNumericValue": 200 }],
        "metrics:cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }]
      }
    },
    "upload": {
      "target": "github"
    },
    "server": {}
  }
}
`}

That's it. Three runs per URL (averages out noise). Assertions fail the build if metrics slip. The GitHub action reports pass/fail as a status check on your PR.

Step 3: Wire it into GitHub Actions.

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

jobs:
  lhci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 20
      - run: npm ci
      - run: npm run build
      - run: npm install -g @lhci/cli@latest
      - run: lhci autorun
        env:
          GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
          LHCI_GITHUB_APP_TOKEN: \${{ secrets.LHCI_GITHUB_APP_TOKEN }}
`}

On every push, the action builds your site, runs Lighthouse three times, compares against your budget, and posts a status check. Red X if you failed. Green check if you passed.

Real Velocity X lhci.json

Here's what we actually use. The numbers are tuned for conversion-focused marketing sites:

{`{
  "ci": {
    "collect": {
      "url": [
        "https://aidxn.com/",
        "https://aidxn.com/brand-strategy",
        "https://aidxn.com/web-design",
        "https://aidxn.com/blog"
      ],
      "numberOfRuns": 3,
      "settings": {
        "chromeFlags": "--disable-gpu --no-sandbox"
      }
    },
    "assert": {
      "assertions": {
        "categories:performance": ["error", { "minScore": 0.95 }],
        "metrics:largest-contentful-paint": ["error", { "maxNumericValue": 1500 }],
        "metrics:interaction-to-next-paint": ["error", { "maxNumericValue": 200 }],
        "metrics:cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }]
      }
    },
    "upload": {
      "target": "github"
    }
  }
}
`}

Four pages: home, two service categories, blog index. Three runs each. If any page misses the budget, the build fails. Simple. Measurable. Enforced.

Six FAQs

Does Lighthouse CI test mobile or desktop?

Both. By default it runs mobile (the harder target). You can override via settings. We only gate mobile because if you pass mobile, desktop almost always passes.

What if a regression is legitimate?

You have two options: (1) fix it, or (2) raise the budget. Raising the budget is a deliberate commit that the team sees. It forces conversation: "Did we agree to slower load times?" Usually, the answer is no, and the regression gets fixed.

How often do you run Lighthouse CI?

On every push and every PR. It takes 2–3 minutes. You see the result before you merge. Catch regressions before they're in main.

Can I test dynamic pages?

Yes, but they need to be pre-built or pre-rendered. Lighthouse CI audits static URLs, not user flows. For Astro static sites, every page renders at build time — test them all. For dynamic dashboards, test the shell and key pages only.

What about third-party script overhead?

That's the point. If you add a tracking script that drops LCP by 300ms, the build fails immediately. You either defer it (Partytown), lazy-load it, or ship without it. The budget forces the conversation.

How do I raise budgets safely?

Commit a lhci.json change with the new threshold and a message like "Raising LCP budget to 1800ms due to new hero video." Team sees it, approves it, it's in the git history forever. Budgets aren't secret.

The Bottom Line

Lighthouse CI turns performance from a "nice to have" into a "ship or don't." Regressions are caught at commit time, not production. Your team stays within budget, your users stay happy, your conversions stay high. Combined with the Core Web Vitals pattern stack, Lighthouse CI ensures the speed stays.

Ready to lock in performance? Check Velocity X pricing to see how automated budgets fit into a modern build pipeline.

Let us make some quick suggestions?
Please provide your full name.
Please provide your phone number.
Please provide a valid phone number.
Please provide your email address.
Please provide a valid email address.
Please provide your brand name or website.
Please provide your brand name or website.