Blogs/Technology

How To Use Puppeteer on AWS Lambda for Headless Automation

Written by Goutham
May 26, 2026
5 Min Read
How To Use Puppeteer on AWS Lambda for Headless Automation Hero

Puppeteer on AWS Lambda is a powerful combination for headless browser automation, with no dedicated servers, automatic scaling, and pay-per-execution billing. This guide walks you through the correct setup, working code, and the config mistakes that break most first attempts.

What is Puppeteer?

Puppeteer is a Node.js library that controls headless Chromium via the Chrome DevTools Protocol. Unlike simple HTTP scrapers, it runs a real browser, handling JavaScript-rendered pages, user interactions, and DOM events.

Common use cases:

  • Screenshots and PDF generation
  • Web scraping of JavaScript-heavy pages
  • Automated UI testing
  • Pre-rendering SPAs for SEO

Why Run AWS Lambda Puppeteer for Headless Automation?

Lambda is event-driven and serverless; it spins up on demand, scales automatically, and charges only for execution time. For bursty automation tasks (screenshot APIs, scheduled scrapes, on-demand PDF generation), it's far more cost-effective than a running EC2 instance.

The challenge is that Lambda has strict constraints: a 250MB unzipped package limit, a default 3-second timeout, and no display server. Getting Puppeteer running correctly requires a few specific decisions.

The Right Package Combination

Most setups break here first.

Don't use the standard puppeteer package, it bundles a full Chromium binary (~170MB) and will exceed Lambda's size limit immediately.

Use instead:

npm install puppeteer-core @sparticuz/chromium
  • puppeteer-core - the full Puppeteer API without a bundled browser
  • @sparticuz/chromium - a Lambda-optimized Chromium binary (~50MB compressed) maintained specifically for serverless environments. See the project on GitHub

This combination stays within Lambda's limits and provides the correct args and executable path for Lambda's runtime.

Setting Up Puppeteer Lambda with Layers

Lambda Layers let you separate heavy dependencies from your function code. Upload Chromium once, attach it to as many functions as you need.

Step 1: Create the Node.js Layer

mkdir nodejs && cd nodejs
npm init -y
npm install puppeteer-core @sparticuz/chromium
cd ..
zip -r puppeteer-layer.zip nodejs/
The nodejs/ folder structure is required — Lambda expects this exact path for Node.js layers.

Step 2: Upload the Layer

Go to Lambda → Layers → Create layer, upload puppeteer-layer.zip, and select Node.js 18.x as the compatible runtime.

Partner with Us for Success

Experience seamless collaboration and exceptional results.

Step 3: Add the Sparticuz Chromium Layer

Add the public Sparticuz Chromium layer by ARN. For ap-south-1:

arn:aws:lambda:ap-south-1:764866452798:layer:chrome-aws-lambda:46

For other regions and the latest version, check Sparticuz/chromium releases.

Step 4: Attach Both Layers to Your Function

In your Lambda function, go to Configuration → Layers → Add a layer and attach both:

  1. The Sparticuz Chromium layer (by ARN)
  2. Your custom Node.js layer (from Step 2)

Step 5: Configure Memory and Timeout

This is where most setups fail silently.

Go to Configuration → General configuration and set:

SettingMinimumRecommended

Memory

1024 MB

2048–3072 MB

Timeout

15 sec

30–60 sec

Memory

Minimum

1024 MB

Recommended

2048–3072 MB

1 of 2

Lambda allocates CPU proportional to memory; more memory means faster browser startup. The default 3-second timeout won't survive a single Chromium launch.

Lambda Handler: Screenshot

import chromium from '@sparticuz/chromium';
import puppeteer from 'puppeteer-core';
export const handler = async (event) => {
  let browser = null;
  try {
    browser = await puppeteer.launch({
      args: chromium.args,
      defaultViewport: chromium.defaultViewport,
      executablePath: await chromium.executablePath(
        '/opt/nodejs/node_modules/@sparticuz/chromium/bin'
      ),
      headless: chromium.headless,
      ignoreHTTPSErrors: true,
    });
    const page = await browser.newPage();
    await page.goto(event.url || 'https://example.com', {
      waitUntil: 'networkidle2',
    });
    const screenshot = await page.screenshot({ encoding: 'base64' });
    return {
      statusCode: 200,
      headers: { 'Content-Type': 'image/png' },
      body: screenshot,
      isBase64Encoded: true,
    };
  } catch (error) {
    console.error(error);
    return { statusCode: 500, body: JSON.stringify({ error: error.message }) };
  } finally {
    if (browser) await browser.close(); // Always close — prevents memory leaks
  }
};

Lambda Handler: PDF Generation

import chromium from '@sparticuz/chromium';
import puppeteer from 'puppeteer-core';
export const handler = async (event) => {
  let browser = null;
  try {
    browser = await puppeteer.launch({
      args: chromium.args,
      executablePath: await chromium.executablePath(
        '/opt/nodejs/node_modules/@sparticuz/chromium/bin'
      ),
      headless: chromium.headless,
    });
    const page = await browser.newPage();
    await page.goto(event.url, { waitUntil: 'networkidle0' });
    const pdf = await page.pdf({
      format: 'A4',
      printBackground: true,
    });
    return {
      statusCode: 200,
      headers: { 'Content-Type': 'application/pdf' },
      body: pdf.toString('base64'),
      isBase64Encoded: true,
    };
  } finally {
    if (browser) await browser.close();
  }
};

Common Errors and Fixes

Error: Failed to launch the browser process. Memory is too low, or executablePath is wrong. Set memory to ≥ 2048 MB and verify the path points to /opt/nodejs/node_modules/@sparticuz/chromium/bin.

Task timed out after 3.00 seconds. Increase the Lambda timeout to at least 30 seconds under Configuration → General configuration.

Cannot find module 'puppeteer-core'.Your layer ZIP is missing the nodejs/node_modules/ folder structure. Repackage with nodejs/ as the root directory.

spawn /opt/chromium ENOENT. The Sparticuz Chromium layer ARN is missing or for the wrong region. Verify both layers are attached.

Pages loading blank or partially. Adjust waitUntil: use networkidle2 for most pages, domcontentloaded for fast scraping, networkidle0 for heavy single-page apps.

Partner with Us for Success

Experience seamless collaboration and exceptional results.

Alternative: Container Image Deployment

If your use case requires custom system dependencies or you're already containerizing Lambda functions, the container image approach sidesteps the 250MB limit entirely (10GB max).

FROM public.ecr.aws/lambda/nodejs:18
RUN dnf install -y chromium
COPY index.mjs ${LAMBDA_TASK_ROOT}/
CMD ["index.handler"]

Set executablePath to /usr/bin/chromium-browser. Pre-installed Chromium also means no extraction overhead at startup, which improves cold start times.

Frequently Asked Questions

Why use puppeteer-core instead of puppeteer on Lambda? 

The standard puppeteer package bundles its own Chromium (~170MB), which exceeds Lambda's 250MB limit. puppeteer-core is the same API without the browser — you supply the executable via a layer.

What is @sparticuz/chromium? 

A community-maintained, serverless-optimized Chromium binary built for Lambda. It exports the correct args, executablePath, and headless values for Lambda's execution environment.

Can Lambda Puppeteer handle concurrent requests? 

Yes, each Lambda invocation runs in its own isolated container, so 100 concurrent requests spin up 100 browser instances in parallel. Always call browser.close() in a finally block.

Is Puppeteer on Lambda suitable for long-running crawls? 

No. Lambda's 15-minute execution limit and per-duration cost make it a poor fit for sustained scraping. Use it for short, on-demand tasks. For long crawls, use ECS Fargate or a dedicated scraping platform.

Final Thoughts

The key decisions that make Puppeteer Lambda work: use puppeteer-core + @sparticuz/chromium, set memory to at least 2048 MB, extend the timeout to 30+ seconds, and always close the browser in a finally block.

Get these right, and you have a scalable, serverless, headless browser that handles bursts automatically, no server management, no Chromium installation headaches.

Author-Goutham
Goutham

Hey, I’m Goutham - a techie who loves simplifying complex ideas. I design systems by day and break down tech jargon by night, always excited to share how awesome tech can be.

Share this article

Phone

Next for you

8 Best GraphQL Libraries for Node.js in 2025 Cover

Technology

Jan 29, 20268 min read

8 Best GraphQL Libraries for Node.js in 2025

Why do some GraphQL APIs respond in milliseconds while others take seconds? The difference often comes down to choosing the right GraphQL library for Node.js. According to npm trends, Apollo Server Express alone sees over 800,000 weekly downloads, proving that developers need reliable tools to build production-ready GraphQL servers. The truth is, building GraphQL APIs in Node.js has never been easier, but picking the wrong library can slow down your entire application. Modern web applications d

I Tested 9 React Native Animation Libraries (Here’s What Works) Cover

Technology

Feb 10, 202614 min read

I Tested 9 React Native Animation Libraries (Here’s What Works)

Why do some mobile apps feel smooth while others feel clunky? I’ve noticed the difference is usually animations under load, especially during scrolling, navigation, and gesture-heavy screens. Google research shows 53% of mobile site visits are abandoned if pages take longer than three seconds to load, and the same performance expectations carry over to mobile apps. The truth is, smooth animations in React Native apps are no longer a luxury; they’re a must-have for a modern, engaging user experi

9 Critical Practices for Secure Web Application Development Cover

Technology

May 18, 20266 min read

9 Critical Practices for Secure Web Application Development

In 2026, developing modern web applications requires a balance between speed and security. Product strategy often pressures development teams to move fast, and ignoring application security can cause catastrophic results. For example, post-credential-based attacks have caused over $5 billion in losses. Security vulnerabilities in web applications are not just technical security problems; they are a business risk. The truth is that security incidents happen when web developers think about web se