All Articles

Running Google Lighthouse from AWS Lambda

Photo by https://unsplash.com/@ketdee
Photo by https://unsplash.com/@ketdee

UPDATE(14 Dec 2019): The post has been updated for node 10 support

TLDR: Use the Lambda layer here to easily run Google Lighthouse from AWS Lambda

Introduction to Google Lighthouse

I’ve recently moved by blog from Medium to my own personal site based on Gatsby.

In order to test my newly fast optimized static site I decided to give Google Lighthouse a try.

To quote Google’s description for Lighthouse:

Lighthouse is an open-source, automated tool for improving the quality of web pages. You can run it against any web page, public or requiring authentication. It has audits for performance, accessibility, progressive web apps, and more.

After making sure my site gets good results on Lighthouse I thought it would make a fun experiment to run Lighthouse audits using AWS Lambda.

Researching Chrome in Lambda

After a quick search I found the lighthouse-lambda package, but it hasn’t been updated recently and will install the chrome binary as a dependency.

As I wanted to keep my Lighthouse lambda package size small, I started looking into creating a Lambda layer with all the required dependencies, most important the Chromium binary due to its size.

I found two relevant projects which include the Chromium binary:

  1. serverless-chrome which I used at first.
  2. chrome-aws-lambda which I use now since it gets frequent releases.

Preparing the Layer

One issue I had with chrome-aws-lambda is that it comes with a compressed version of the Chromium binary and extracts it on demand while running the function.

Since I didn’t want my Lambda decompressing files, I preferred to ship the layer with an uncompressed version of the binary using a simple postInstall script.

chrome-aws-lambda describes a way to create a Lambda layer with the uncompressed version of chromium but it requires having brotli installed, will package puppeteer-core and will still copy some files around when invoking the function

The next step was to use the Serverless framework to set up my layer.

Here is the layer serverless.yml:

service: lighthouse-layer

provider:
  name: aws
  runtime: nodejs10.x
  stage: ${opt:stage, 'dev'}
  region: ${opt:region, 'us-east-1'}

layers:
  lighthouse:
    path: ./layer
    description: Layer with all the required dependencies to run google lighthouse
    compatibleRuntimes:
      - nodejs10.x
    licenseInfo: MIT
    retain: true

The last step was to run sls deploy and the layer was ready.

Creating a Lambda to Run Google Lighthouse

This was pretty straightforward once I had the layer deployed.

Adding a layer to a Lambda function is a piece of cake with the help of the Serverless framework.

Here are the relevant parts of my serverless.yml file:

functions:
  runner:
    timeout: 600
    memorySize: 1024
    reservedConcurrency: 10
    batchSize: 1

    # other settings omitted for brevity

    events:
      - sqs:
          arn: !GetAtt ApiGatewayIntegrationSqsAudit.Arn

    description: Runs Google lighthouse

    handler: src/runner.handler

    layers:
      # you should replace this with your layer's ARN which you'll get after you deploy it
      - arn:aws:lambda:${self:provider.region}:#{AWS::AccountId}:layer:lighthouse:8

Notice I’m putting the Lambda behind an SQS queue to throttle it. I’m using a plugin to make it easier to create the SQS queue.

After running sls deploy I was able to invoke my Lambda and have it output the Google Lighthouse audit results. Finally I can relax with a emoji-beer

Summary

While using AWS Lambda to run a Google Lighthouse audit was a fun experiment, I think a better approach will be to use a CI/CD service and run Lighthouse from the service.
Another alternative option is to use Google Cloud Functions that should have headless Chrome support out of the box.
A braver approach will be to use this experimental project from Google.

If you do decide to use the AWS Lambda solution you should be aware of the following caveats:

Congratulations! You’ve made it to the end of the post emoji-tada

Please let me know what you think of the post in the comments, or directly on Twitter. The links are below