Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Run Playwright in Lambda by shipping your handler, a pinned Playwright package, matching browser binaries, Linux dependencies, and Lambda’s runtime interface client in a container image. Build for the function architecture, test through the Lambda runtime interface emulator, then deploy the image from Amazon ECR. Playwright is headless by default; install Xvfb and wrap the command with xvfb-run only when your workload truly requires a headed browser.

What the Lambda container must contain

A Lambda container is immutable at invocation time, so everything needed to launch the browser belongs in the image. That includes:

  • Your application and Lambda handler.
  • A pinned Playwright language package.
  • Browser executables installed for that exact Playwright version.
  • System libraries required by the selected browser.
  • Xvfb, but only if headed execution is part of the design.
  • The Lambda runtime interface client when the base image is not an AWS language base image.

Playwright’s published images bundle browser binaries and system dependencies, while the Playwright package is installed by your project. Keep those versions aligned: if the package and image come from different Playwright releases, Playwright may look for an executable at a path that does not exist.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Lambda accepts Docker and OCI images and limits the uncompressed image, including all layers, to 10 GB. The image must also target the architecture configured for the function, such as linux/amd64 or linux/arm64.

Choose headless or headed execution

Headless is the normal Lambda path

Playwright launches browsers headless by default. Screenshots, PDF generation, DOM inspection and most automation tasks work without an X server. Leaving headed mode disabled avoids an extra process, package and failure point.

Use Xvfb only for headed behavior

Some sites or test suites need a real display, headed browser flags or visual behavior that differs in headless mode. On Linux, headed execution requires Xvfb. The documented pattern is to run the Playwright command through xvfb-run, which creates a temporary virtual display and sets the display environment for the child process.

Build a Node.js Lambda image

The following example uses a Playwright Ubuntu image as the operating-system layer. It installs the Lambda runtime interface client because this is a non-AWS base image. The version shown is an example pin; choose one version and use it consistently in the Docker image and package.json, then rebuild whenever you change it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Project files

Create a directory containing package.json, index.mjs and Dockerfile.

{
  "name": "lambda-playwright",
  "version": "1.0.0",
  "type": "module",
  "dependencies": {
    "@aws-lambda-nodejs-runtime-interface-client": "3.0.0",
    "playwright": "1.55.0"
  }
}

Pin the runtime-interface-client version according to your normal dependency policy. The important compatibility rule is that the Playwright package version and the browser files installed in the image are the same release.

Lambda handler

import { chromium } from 'playwright';

export const handler = async (event) => {
  const target = event?.url || 'https://example.com';
  const browser = await chromium.launch({ headless: true });
  try {
    const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
    await page.goto(target, { waitUntil: 'networkidle', timeout: 45000 });
    const title = await page.title();
    const screenshot = await page.screenshot({ type: 'png' });
    return {
      statusCode: 200,
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ title, pngBase64: screenshot.toString('base64') })
    };
  } finally {
    await browser.close();
  }
};

Always close the browser in a finally block. A reused Lambda execution environment can otherwise accumulate browser processes across invocations.

Dockerfile with Xvfb available

FROM mcr.microsoft.com/playwright:v1.55.0-jammy

WORKDIR /var/task

RUN apt-get update 
    && apt-get install -y --no-install-recommends xvfb 
    && rm -rf /var/lib/apt/lists/*

COPY package*.json ./
RUN npm ci --omit=dev

# Reinstalling with the pinned package makes the browser/package relationship explicit.
RUN npx playwright install chromium

COPY index.mjs ./

ENV NODE_ENV=production
ENTRYPOINT ["/usr/local/bin/npx", "aws-lambda-ric"]
CMD ["index.handler"]

The Playwright image already contains browser dependencies, and the explicit playwright install chromium step ensures the browser associated with the package in package.json is present. If your selected image uses a different Node or Playwright layout, verify the paths and runtime-interface-client entry point before publishing.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Run headed Playwright through Xvfb

Do not add --headless=false to every invocation by default. Keep the handler headless unless headed behavior is required. For a headed test command inside the image, use:

xvfb-run --server-args="-screen 0 1440x900x24" npx playwright test

For a custom Node script, the equivalent is:

xvfb-run --server-args="-screen 0 1440x900x24" node headed-script.mjs

If Xvfb is installed but the command is not wrapped, the browser still has no display and launch fails. Conversely, wrapping a headless workload adds complexity without improving the result.

Build for Lambda’s architecture

Build on the same architecture selected for the function. For an x86_64 function:

docker buildx build 
  --platform linux/amd64 
  --provenance=false 
  -t lambda-playwright:latest 
  --load .

For an arm64 function, change the platform to linux/arm64. AWS documents the --provenance=false option as required for Lambda-compatible images. A mismatch can produce an image that builds successfully but is rejected or cannot start in Lambda.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep the uncompressed image below 10 GB. A multi-stage build can remove compilers, package caches and development files, but do not remove browser libraries that your selected executable needs.

Test the image locally with Lambda’s runtime interface emulator

Run the image with the Lambda runtime interface emulator (RIE) mounted into the container. The exact RIE binary path depends on where you downloaded it; the important part is that the container listens on port 8080 and receives the Lambda invocation protocol.

docker run --rm 
  -p 9000:8080 
  --init 
  --ipc=host 
  lambda-playwright:latest

Playwright recommends --init for correct PID 1 process handling and --ipc=host for Chromium during local Docker runs. Invoke the local endpoint in a second terminal:

curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" 
  -H "content-type: application/json" 
  -d '{"url":"https://example.com"}'

Before deployment, confirm that navigation, screenshots or PDFs, fonts, timeout behavior and temporary-file handling work under the same architecture you will configure in Lambda. Set DEBUG=pw:browser when you need Playwright browser-launch diagnostics.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Deploy through Amazon ECR

  1. Create an ECR repository in the AWS Region where the function will run.
  2. Authenticate Docker to that registry with the ECR login command for your account and Region.
  3. Tag the local image with the complete ECR repository URI.
  4. Push the tag.
  5. Create a Lambda function using the pushed container image, or update an existing function to that image digest.
  6. Select the same architecture used by docker buildx.

Set memory and timeout from measurements of your browser startup and page workload rather than a generic number. Monitor cold starts, browser crashes, invocation timeouts, /tmp usage and whether every browser process is closed. Rebuild after Playwright, browser or operating-system dependency changes; do not silently mix browser files from one release with a package from another.

Python alternative

If your handler is Python, use the same container principles: pin playwright, install its browser in the image, add Xvfb when needed, and include the Python runtime interface client when the base image is not an AWS Python image.

from playwright.async_api import async_playwright

async def handler(event, context):
    url = event.get("url", "https://example.com")
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        try:
            page = await browser.new_page(viewport={"width": 1440, "height": 900})
            await page.goto(url, wait_until="networkidle", timeout=45000)
            return {"statusCode": 200, "body": await page.title()}
        finally:
            await browser.close()

Use the Python runtime interface client and command appropriate to the Python base you selected. Do not copy the Node entry point into a Python image.

Browser and architecture compatibility

Chromium

Chromium is the usual first target for Lambda because the Playwright container workflow and local Docker diagnostics are commonly exercised with it. If Chromium crashes locally, first retry with --init and --ipc=host, then reproduce through the Lambda emulator.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

WebKit and Firefox

A community Lambda container example reports Chromium and WebKit working while Firefox required additional tuning in that project. That is implementation evidence, not a universal compatibility promise. Validate the exact Playwright version, base image, architecture and browser you intend to deploy.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting

“Executable doesn’t exist” or browser not found

The package and browser image are probably on different Playwright versions, or the browser was never installed in the final image. Pin one version, run the matching playwright install command during the image build, and make sure a later build stage does not discard the browser directory.

Chromium crashes or runs out of memory

Re-run locally with --init and --ipc=host. Check the Lambda memory setting, reduce concurrent pages, close every browser and page, and inspect DEBUG=pw:browser output. A larger image does not automatically provide more runtime memory.

Headed launch reports no display

Install Xvfb, confirm that it is present in the final image, and invoke the headed command through xvfb-run. A manually set DISPLAY value without a running X server is not sufficient.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Lambda rejects the image

Rebuild with the function architecture and --provenance=false. Check that the pushed tag points to the image you built, that the repository is in the intended Region and that the uncompressed image is under Lambda’s 10 GB limit.

Best Value
Docker Container Linux Devops Programming Coding T-Shirt
  • Docker, Docker Swarm, Docker Compose, Programmer, Developer, Coding, Programming, Software Engineer, Code, DevOps, Deploy, Deployment, Kubernetes, Salt, Puppet, Chef, Terraform, Container, AWS, Azure, Cloud, Geek, Funny, Computer, Software, Tech, IT
  • Integration, Scrum, Compile, Compilation, Science, Bug, Debug, Python, Linux, Java, Javascript, Scala, Dotnet, Kotlin
  • Lightweight, Classic fit, Double-needle sleeve and bottom hem

Firefox works locally but not in Lambda

Treat browser support as version- and image-specific. Reproduce with the exact image and architecture, inspect launch logs, and either apply the additional tuning your chosen combination requires or use a browser with verified behavior for your workload.

Image is too large or cold starts are slow

Use a multi-stage build, remove package caches and development-only assets, install only the browsers you need and keep the final image below the documented limit. Measure startup and page-load time after each change instead of assuming that a smaller image will improve every workload.

Or skip the browser setup

If your goal is reliable website screenshots rather than maintaining browsers inside Lambda, ScreenshotNeo is the first service to try: it removes common consent banners, popups and chat widgets before capture, bills only clean shots, and has the lowest paid plan among the stated options.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

One GET request returns a PNG, JPEG, WebP or PDF:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for all options, including full-page and selector captures, device and retina settings, dark mode, custom CSS and JavaScript, clicks, waits, blocked resources, headers, cookies, user agents, geolocation, PDF controls, caching, signed links, asynchronous webhooks, bulk capture and usage reporting.

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}`);
const body = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', body));

Responses identify whether a shot was clean and billable with the X-Page-Verdict and X-Billed headers. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed. ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

The Bottom Line

Use a pinned, version-matched Playwright container, build it for Lambda’s architecture with --provenance=false, test it through the Lambda runtime emulator, and add Xvfb only for genuinely headed workloads.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.