Web Browsing with Playwright on Lambda

awsnode.js

Run Playwright on Lambda. Since only /tmp is writable, launch Chromium with –disable-dev-shm-usage.

import { chromium, Browser, Page } from "playwright-chromium";

browser = await chromium.launch({
  headless: true,
  args: [
    "--no-sandbox",
    "--disable-setuid-sandbox",
    "--disable-dev-shm-usage",
    "--single-process",
  ],
});

const page = await browser.newPage();

await page.goto("https://example.com", {
  waitUntil: "networkidle",
});

Build the application and the binaries that aws-lambda-ric depends on, then copy them into the official Playwright image. aws-lambda-ric is a package for running Lambda functions on a custom base image, connecting the application to the Lambda Runtime API.

Chromium is already installed, so there is no need to run npx playwright install --with-deps chromium. The image version is matched with the playwright library version.

FROM node:24-slim AS builder

# builder image doesn't need chromium so prevent from being installed by postinstall
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1

# for building curl/aws-lambda-cpp on aws-lambda-ric preinstall
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
      cmake make g++ python3 xz-utils ca-certificates && \
    rm -rf /var/lib/apt/lists/*

RUN corepack enable && corepack prepare pnpm@latest --activate

WORKDIR /repo

COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
COPY test-app/package.json ./test-app/

RUN pnpm install --frozen-lockfile --filter test-app...

COPY test-app/tsconfig.json ./test-app/
COPY test-app/src ./test-app/src
RUN pnpm --filter test-app run build

RUN pnpm deploy --filter test-app --prod /out

FROM mcr.microsoft.com/playwright:v1.59.1-noble

WORKDIR /var/task

COPY --from=builder /out/node_modules ./node_modules
COPY --from=builder /out/package.json ./package.json
COPY --from=builder /repo/test-app/dist/ ./

ENTRYPOINT ["/var/task/node_modules/.bin/aws-lambda-ric"]
CMD ["handler.handler"]

If invoking after deployment results in an Unhandled error, check that files are placed at the right paths.

new lambda.DockerImageFunction(this, "TestAppFunction", {
  functionName: "test-app",
  code: lambda.DockerImageCode.fromImageAsset(path.join(__dirname, "../.."), {
    file: "test-app/Dockerfile",
  }),
  memorySize: 2048,
  timeout: cdk.Duration.seconds(180),
  architecture: lambda.Architecture.X86_64,
});