For most Node.js Lambda functions, bundle Chromium in a Linux-built Lambda layer and keep your function code in a separate ZIP. Put layer dependencies under nodejs/node_modules, attach the published layer, and launch @sparticuz/chromium through puppeteer-core. If the browser and its dependencies will not fit your ZIP-and-layer packaging, put the runtime, application, and browser in a Lambda container image instead. The right choice depends on package size, reuse, runtime, and architecture—not on an assumed performance advantage.
Choose a Lambda layer or a container image
Both patterns can bundle a serverless Chromium build with an automation client. A layer is usually the simpler starting point when you want to reuse the browser dependencies across ZIP-deployed functions. A container image is the practical alternative when the browser stack is too large or you want the runtime, app, and dependencies built into one deployable artifact.
| Decision | ZIP function plus layer | Lambda container image |
|---|---|---|
| Where dependencies go | Function dependencies go in the function ZIP; shared dependencies go in a layer ZIP using Lambda’s required directory layout. | The runtime, application, Chromium, and dependencies are built into the image. |
| Reuse | A published layer can be attached to multiple functions. A function can use up to five layers. | Reuse the image through your image-tag or digest and registry workflow. |
| Packaging limits | Must fit the applicable ZIP and aggregate uncompressed package limits. A full browser can make ZIP packaging difficult. | Lambda supports container images up to 10 GB uncompressed. |
| Layer attachment | Attach a published layer version to the function. | Lambda container-image functions cannot have layers attached; include dependencies in the image. |
| Best fit | Several ZIP-based functions share a browser build, and the packages fit. | The browser stack is large or you want a single immutable deployment artifact. |
These packaging limits are ceilings, not a promise that a particular Chromium build will fit. Measure the actual ZIP and extracted contents you plan to deploy. If the ZIP approach does not fit after removing unnecessary files, switch to a container rather than trying to force an oversized artifact into a layer.
Check runtime, Linux environment, and architecture first
Do this before installing packages: a correctly packaged browser for the wrong runtime or CPU architecture is still an unusable deployment.
Free tools Windows power users keep installed
One-click scans. No signup required.
#1 Best Overall
- Node.js runtime: Build Node.js layer packages for the same Node.js version as the Lambda function. Use Lambda’s expected top-level path:
nodejs/node_modulesor the runtime-specificnodejs/nodeX/node_modulesform. - Operating system: Build and package in a Linux environment compatible with Lambda. Lambda runs on Amazon Linux; a package assembled on a developer’s desktop operating system is not automatically compatible.
- CPU architecture: Confirm whether the function is
x86_64orarm64, then choose matching Chromium artifacts and build the image or layer for that architecture. The Sparticuz npm package provides x64 binaries; its documentation describes separate arm64 layer or remote-pack options. - Automation client: Use
puppeteer-coreor Playwright as the client. Do not assume that the ordinary Puppeteer package and its browser-download behavior are what you want in a Lambda bundle.
The examples below use Node.js, puppeteer-core, and @sparticuz/chromium. Keep the Chromium and automation-client versions pinned and test them together before deployment. Sparticuz follows Chromium’s release cycle rather than ordinary semantic versioning, and breaking changes can occur at patch level.
Build a Chromium Lambda layer for a ZIP-deployed function
This pattern keeps the browser package in a separately published layer and the function’s automation client and handler in the function ZIP. Run the packaging commands in a Lambda-compatible Linux build environment, with a Node.js version matching the function runtime.
1. Install the layer dependency in Lambda’s expected path
From a clean build directory, create the required structure and install the Chromium package into it. The example installs a production dependency directly into the layer’s nodejs directory:
mkdir -p layer/nodejs
npm install --prefix layer/nodejs --omit=dev @sparticuz/chromium
cd layer
zip -r ../chromium-layer.zip nodejs
Inspect the ZIP before publishing. Its top level should contain nodejs/, with the package beneath nodejs/node_modules/; do not accidentally ZIP an enclosing build directory so that the archive starts with layer/nodejs/.
2. Publish the layer and attach its version
Publish chromium-layer.zip as a Lambda layer for the function’s compatible runtime and architecture. Attach the resulting layer version to the function. Lambda extracts layer content under /opt; with this directory layout, Node.js can resolve the package from the layer’s module path. Keep track of the layer version ARN used by each function, since the function refers to a published version rather than an editable local directory.
Rank #2
Layers can be shared by multiple ZIP-deployed functions, but a function can attach no more than five. If your deployment already uses layers, count them before adding another.
3. Package the client and handler in the function ZIP
Install the automation client as a production dependency and package the handler with it. If Chromium is supplied by the layer, the Sparticuz README allows @sparticuz/chromium to be a development dependency in the function package; this example omits it from the function ZIP because the layer supplies it.
mkdir function
cd function
npm init -y
npm install --omit=dev puppeteer-core
Create index.js with the following handler. It opens one page, captures the requested URL, and closes the browser even if navigation or capture fails:
const chromium = require('@sparticuz/chromium');
const puppeteer = require('puppeteer-core');
exports.handler = async (event) => {
const url = event.url;
if (typeof url !== 'string' || !/^https?:///i.test(url)) {
throw new Error('Provide an http or https URL in event.url');
}
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: 'networkidle0' });
const image = await page.screenshot({ type: 'png', fullPage: true });
return {
statusCode: 200,
headers: { 'content-type': 'image/png' },
isBase64Encoded: true,
body: image.toString('base64'),
};
} finally {
if (browser) await browser.close();
}
};
Set the Lambda handler to index.handler and ZIP the contents of the function directory, not the directory itself:
zip -r ../function.zip .
The sample returns a base64-encoded PNG in the Lambda response shape used by integrations that expect binary bodies. If your caller or integration uses a different response contract, adapt that wrapper; the screenshot bytes themselves are produced by Puppeteer. For an internal worker that stores or forwards the bytes another way, replace the return object while retaining the browser cleanup.
Rank #3
4. Invoke and verify the deployed function
Invoke the deployed function with an event such as {"url":"https://example.com"}. Confirm that it returns a PNG body and that the logs do not report a missing executable, an incompatible binary, or a module-resolution error. If the local package works but the deployed one fails, check the ZIP’s top-level paths, attached layer version, runtime, and architecture before changing browser launch options.
Use a Lambda container image when ZIP packaging does not fit
A container image puts the runtime, application, browser, and Node.js dependencies together. This avoids attaching a layer and gives you one artifact to build and deploy. Lambda’s documented maximum for an uncompressed container image is 10 GB. The image still needs a Chromium binary that matches the function’s architecture and a Linux environment compatible with Lambda.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →With an AWS Lambda Node.js base image, a minimal Dockerfile can install the two production dependencies and copy in the handler:
FROM public.ecr.aws/lambda/nodejs:20
WORKDIR ${LAMBDA_TASK_ROOT}
COPY package*.json ./
RUN npm ci --omit=dev
COPY index.js ./
CMD ["index.handler"]
For this image pattern, put both packages in the image’s production dependencies. A suitable package.json includes puppeteer-core and @sparticuz/chromium; commit the lockfile so npm ci installs the versions you tested.
npm install --save-exact puppeteer-core @sparticuz/chromium
Use the same index.js handler shown for the layer approach. Build and publish the image for the Lambda architecture selected for the function, then configure the function to use the image. The AWS Lambda base image supplies the Lambda runtime environment; if instead you choose an OS-only or alternative base image, include the Lambda runtime interface client as required for that base-image approach.
Do not attach layers to a container-image function. If you want to share a common browser build among image-based functions, manage that through your image build and registry workflow, not through Lambda layer attachment.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Size, reliability, and cost decisions
Keep the artifact lean without removing required browser files
Browser packages include more than the JavaScript import: they need the matching executable and supporting files. Remove development dependencies and unused browser assets from your deployment, but do not prune files blindly from Chromium’s package. Build the exact artifact you intend to deploy, inspect its contents, and test that artifact on Lambda. When the resulting ZIP and layers cannot fit the applicable limits, use the container route.
Make browser lifecycle and navigation behavior explicit
Launching a browser and navigating to a page can fail independently. Keep browser closure in a finally block so errors do not skip cleanup. The sample uses networkidle0 to wait for network activity to quiet down; pages with long-lived requests or continually loading content may not reach that condition. For those pages, choose a wait condition that matches the capture you need, and test it with the target site. No one wait strategy or package choice guarantees a particular startup time or capture duration.
Pin versions and test upgrades
Record the Node.js runtime, function architecture, Chromium package version, automation-client version, and layer or image revision used for a release. Upgrade them deliberately as a tested set. Because Sparticuz versions track Chromium releases and may include breaking changes in patch releases, do not treat a routine patch update as automatically risk-free.
Compare deployment friction, not invented performance claims
A layer can reduce duplicated browser packaging across ZIP functions; a container can simplify delivery when the dependency bundle is large. Neither packaging choice alone establishes which function will start or capture faster. The cited package and AWS documentation do not provide a universal performance result for these deployment patterns, so measure startup, navigation, memory, and timeouts with your own page mix and Lambda configuration before making a performance-based decision.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallBest Value
Troubleshoot common packaging and launch failures
| Symptom | Likely cause | What to check or change |
|---|---|---|
Cannot find @sparticuz/chromium |
The layer ZIP has an extra enclosing directory, the layer is not attached, or the package was omitted from the image. | Check for nodejs/node_modules/@sparticuz/chromium at the layer ZIP root, confirm the function uses the intended published layer version, or install it as an image production dependency. |
| Chromium executable is missing or cannot start | The binary was not included, the deployment was built for an incompatible environment, or the selected artifact does not match the function architecture. | Rebuild in a Lambda-compatible Linux environment, verify the architecture, and use the package’s args, defaultViewport, and executablePath() configuration. |
| Layer or ZIP deployment is rejected for size | The browser and other dependencies exceed an applicable ZIP/layer package limit. | Remove development files and unused assets, inspect both compressed and extracted contents, then move to a container image if the required package still will not fit. |
| Function reports too many layers | Adding the Chromium layer would exceed Lambda’s five-layer-per-function maximum. | Consolidate compatible dependencies into a layer or use a container image, which packages dependencies directly and does not use attached layers. |
| Navigation hangs or times out | The page may not become network-idle, may depend on slow or blocked resources, or may be taking longer than the function’s configured time allows. | Check the navigation wait condition and page behavior, then adjust the wait strategy or Lambda timeout to the workload. Test against the pages you actually need to capture. |
| Works in one deployment but not another | Runtime, package version, architecture, or layer/image build differs. | Compare the deployed runtime and architecture, inspect the artifact actually published, and pin and retest the Chromium and client versions together. |
Or skip the browser setup
If the job is simply to get a clean website screenshot, ScreenshotNeo is a website screenshot API and MCP server: a GET request with a URL can return PNG, JPEG, WebP, or PDF without you packaging Chromium into Lambda. See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; those steps can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and responses identify page verdict and billing with X-Page-Verdict and X-Billed headers. Its MCP server offers take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo and get 1,000 free screenshots a month with no card.
FAQ
Can I use Playwright instead of Puppeteer?
Yes. The Chromium package is intended to pair with Puppeteer or Playwright. The code here is specifically for Puppeteer; with Playwright, use its Chromium launch configuration and the executable supplied by the package, and verify the exact combination of versions and architecture in your deployment.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesDoes a container image need a Lambda runtime interface client?
That depends on the base image. The AWS Lambda Node.js base image provides the Lambda runtime environment. An OS-only or alternative base image needs a runtime interface client as part of that approach.
Can a layer package be used by multiple functions?
Yes. A published Lambda layer can be shared across functions, provided each consuming function is compatible with the layer’s runtime and architecture and remains within the per-function layer limit.
Frequently Asked Questions
Can I use Playwright instead of Puppeteer?
Yes. The Chromium package is intended to pair with Puppeteer or Playwright. The code here is specifically for Puppeteer; with Playwright, use its Chromium launch configuration and the executable supplied by the package, and verify the exact combination of versions and architecture in your deployment.
Does a container image need a Lambda runtime interface client?
That depends on the base image. The AWS Lambda Node.js base image provides the Lambda runtime environment. An OS-only or alternative base image needs a runtime interface client as part of that approach.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Can a layer package be used by multiple functions?
Yes. A published Lambda layer can be shared across functions, provided each consuming function is compatible with the layer’s runtime and architecture and remains within the per-function layer limit.
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.

