Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Use either a Lambda layer with a ZIP-deployed function or a Lambda container image. A layer is the reusable option: build a Linux-compatible archive with the required Node.js directory, publish it, and attach its versioned ARN. A container image is the practical fallback when Chromium and its libraries make ZIP limits awkward; the runtime, application, browser, and dependencies are built into one image.
This guide shows both deployment paths with @sparticuz/chromium and puppeteer-core, explains architecture and version matching, and gives fixes for the failures that commonly appear only in Lambda.
Choose the packaging model first
| Concern | ZIP function plus layer | Container image |
|---|---|---|
| Reuse | A published layer can be attached to several functions. | Reuse an image tag or digest through your container registry. |
| Where dependencies live | Function dependencies are in the deployment ZIP; shared browser files are in the layer and extracted under /opt. |
The runtime, application, Chromium, and all libraries are inside the image. Layers cannot be attached. |
| Size pressure | Subject to Lambda ZIP, layer, and aggregate uncompressed limits; a browser often makes this route difficult. | Lambda allows up to 10 GB uncompressed for a container image. |
| Best fit | Several functions need the same browser build and the package fits comfortably. | The browser stack is large or you want one immutable, reproducible artifact. |
| Architecture | Publish a layer built for the function’s x86_64 or arm64 architecture. |
Build the image and include Chromium binaries for the selected Lambda architecture. |
Lambda permits up to five layers on one function. A layer is a ZIP archive containing supplementary code or data; Lambda unpacks it under /opt. For Node.js, the archive must use the runtime-specific layout described below.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Prerequisites and compatibility checks
- Choose the exact Lambda Node.js runtime before installing packages. Build Node.js layer content with the same runtime version used by the function.
- Build on Linux compatible with Lambda’s Amazon Linux environment. A package assembled on macOS or Windows can contain incompatible native modules.
- Confirm the function architecture in Lambda configuration:
x86_64andarm64require different Chromium artifacts. - Use
puppeteer-coreor Playwright as the automation client and@sparticuz/chromiumas the serverless Chromium distribution. - Pin the Chromium and automation-client versions. Sparticuz follows Chromium releases rather than ordinary semantic versioning, so a patch-level update can contain breaking changes.
@sparticuz/chromium is not tied to a particular Puppeteer version, but compatibility still has to be checked for the versions you select. Its minimal distribution can also use a remotely hosted Chromium pack; that option requires the network access and configuration documented by the project.
#1 Best Overall
Pattern 1: package Chromium in a Lambda layer
Build the layer directory
For a Node.js layer, place dependencies under nodejs/node_modules. Some runtimes also support a runtime-specific path such as nodejs/node20/node_modules; use the convention required by your selected runtime.
- Start a Linux build environment matching Lambda’s operating system and architecture. A CI runner or an Amazon Linux-compatible container avoids native-binary surprises.
- Create the layer tree and install production dependencies:
mkdir -p layer/nodejs
cd layer/nodejs
npm init -y
npm install --omit=dev puppeteer-core @sparticuz/chromium
cd ../..
zip -r chromium-layer.zip nodejs
If the browser is supplied entirely by a separate layer, keep @sparticuz/chromium as a development dependency in the function package only when that layer’s documentation explicitly supports it. Otherwise, include the package in the production layer as shown.
Publish and attach the layer
- In the AWS Lambda console, open Layers and choose Create layer.
- Upload
chromium-layer.zip, select compatible runtimes, and select the matching architecture. - Create the layer, then open the function’s Code or Configuration page, choose Layers, and add the layer by ARN and version.
- Deploy the function ZIP containing your handler and any application-only dependencies.
Lambda extracts the layer at /opt. Do not hard-code a path from your workstation; use the Chromium package’s executablePath helper, which resolves its unpacked location for the invocation.
Node.js handler using Puppeteer
Set the handler to index.handler and use this complete example:
const chromium = require('@sparticuz/chromium');
const puppeteer = require('puppeteer-core');
exports.handler = async (event) => {
const url = event.url || 'https://example.com';
let browser;
try {
browser = await puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath(),
headless: true
});
const page = await browser.newPage();
await page.goto(url, {waitUntil: 'networkidle2', timeout: 30000});
const png = await page.screenshot({type: 'png', fullPage: true});
return {
statusCode: 200,
headers: {'content-type': 'image/png'},
isBase64Encoded: true,
body: png.toString('base64')
};
} finally {
if (browser) await browser.close();
}
};
The finally block matters: a warm execution environment can be reused, and leaked browser processes consume memory and file descriptors. Set Lambda memory and timeout high enough for your pages, but do not assume a universal startup or rendering time; it depends on the page, architecture, browser build, and concurrency.
Rank #2
Pattern 2: build a Lambda container image
Use this route when the ZIP and layer aggregate limits cannot accommodate the browser or when you want the complete runtime stack versioned as one artifact. A container-image function cannot have layers attached.
Dockerfile based on the AWS Node.js image
Place index.js and package.json beside this Dockerfile. The AWS base image supplies the Lambda runtime interface:
Free tools Windows power users keep installed
One-click scans. No signup required.
FROM public.ecr.aws/lambda/nodejs:20
COPY package*.json ${LAMBDA_TASK_ROOT}/
RUN npm ci --omit=dev
COPY index.js ${LAMBDA_TASK_ROOT}/
CMD ["index.handler"]
Your package.json should list pinned production versions:
{
"type": "commonjs",
"dependencies": {
"@sparticuz/chromium": "PINNED_VERSION",
"puppeteer-core": "PINNED_VERSION"
}
}
Build and push the image with a tag or digest, then create or update the Lambda function from that image. Build for the same architecture selected in Lambda; an image containing x64 Chromium cannot run as an arm64 function. If you use an OS-only or alternative base image instead of an AWS language base image, add the Lambda runtime interface client and configure its entrypoint as required for that base.
Runtime options that affect real pages
Viewport, full-page output, and lazy content
Use chromium.defaultViewport as a safe baseline, then set an explicit viewport for deterministic layouts. Full-page screenshots can trigger lazy-loaded assets and increase memory use. For long pages, consider capturing a selected element or splitting the work across invocations.
Rank #3
Navigation and network behavior
networkidle2 can wait indefinitely on applications that maintain open connections. Use a finite timeout and a more suitable lifecycle event when necessary. Pages protected by bot checks or CAPTCHAs may never produce usable output; treat those responses as an application-level failure rather than retrying indefinitely.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Temporary storage and concurrency
Chromium unpacks files during startup and uses temporary storage. Ensure the function’s ephemeral storage is sufficient for your selected browser distribution and page workload. High concurrency multiplies memory and temporary-file demand; set reserved or account concurrency deliberately and monitor errors before increasing it.
Size, version, and release management
- Install with production-only dependencies and remove test fixtures, documentation, and unused browser assets from ZIP builds.
- Keep the browser package and automation client pinned in source control. Upgrade them together in a test function.
- Record the Lambda runtime, architecture, Chromium package version, Puppeteer version, and layer version in deployment metadata.
- Publish a new layer version rather than mutating an existing one, so rollback is a single configuration change.
- Use a container image digest for repeatable deployments instead of relying only on a mutable tag.
Troubleshooting common failures
“Cannot find module” or missing executable
Cause: the layer ZIP has an extra top-level directory, the Node.js path is wrong, or the layer is not attached to the published function version.
Fix: open the ZIP and verify that nodejs/node_modules is at its root, confirm the layer ARN and version on the deployed function, and log await chromium.executablePath() during a test invocation.
Exec format error
Cause: an x64 binary is running on arm64, or the reverse.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesFix: align the Lambda architecture, layer or image platform, and Chromium artifact. Rebuild native dependencies in the target architecture.
Browser closes immediately
Cause: missing launch arguments, an incompatible Chromium/client pair, insufficient memory, or a corrupted unpack directory.
Fix: use the package-provided args, defaultViewport, and executablePath; verify pinned versions; increase memory for a diagnostic invocation; and ensure temporary storage is writable.
Timeouts on page.goto
Cause: slow third-party resources, persistent connections, bot challenges, or a lifecycle condition that never becomes idle.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Fix: set a finite navigation timeout, choose domcontentloaded where appropriate, wait for a specific selector, and capture diagnostic logs. Do not treat a CAPTCHA as a transient network error.
Best Value
ZIP or layer size rejection
Cause: the browser and dependencies exceed the ZIP or aggregate uncompressed limits.
Fix: remove development files and unused assets, split genuinely shared dependencies into layers (within Lambda’s five-layer maximum), or move the complete stack to a container image, which supports up to 10 GB uncompressed.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.When you do not need to operate Chromium yourself
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF, while its capture flow accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
Recommended Free Tools
For the one-call API, see the ScreenshotNeo documentation:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Every plan includes the same features, including selectors, dark mode, device presets, custom CSS and JavaScript, request blocking, cookies and headers, geolocation, PDFs, signed links, async jobs, bulk capture, caching, and a usage API. The Free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
Frequently Asked Questions
Can a container-image Lambda also use a Chromium layer?
No. Container-image functions package dependencies in the image and do not support attached Lambda layers.
Is Playwright required for this setup?
No. The documented example uses Puppeteer Core, while the Sparticuz distribution can also be used with Playwright when its launch configuration and version compatibility are satisfied.
Which architecture should I choose?
Choose the architecture supported by your surrounding dependencies and deployment environment, then use the matching Chromium artifact consistently in the layer or image.
Does a newer Sparticuz patch release guarantee compatibility?
No. The project follows Chromium releases rather than standard semantic versioning, so review its compatibility guidance and release notes before upgrading.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

