ScreenshotNeo

BlogHow-to

How to Run Playwright in Docker on AWS

Build a version-pinned Playwright container and run it as an ECS task on AWS Fargate, with practical guidance for IAM, networking, storage, security, and troubleshooting.

By the ScreenshotNeo team4 October 202611 min read

To run Playwright in Docker on AWS, build an image with a pinned Playwright version, push it to Amazon ECR, and run it as an Amazon ECS task on AWS Fargate. The official Playwright image supplies browser binaries and system dependencies, but your application must install the matching Playwright package. Fargate requires awsvpc networking and task-level CPU and memory; local Docker settings such as --ipc=host do not translate into Fargate task settings.

This guide uses a finite test batch as the example. Use an ECS task for scheduled, triggered, or finite work that exits after reporting results. Use an ECS service when you need ECS to maintain a desired task count or keep a worker available. Fargate is one deployment option; the sources do not establish that it is the cheapest or best choice for every workload.

1. Choose how the browser workload will run

Workload Starting point Why
Scheduled checks, CI batches, or a test run started by an event ECS task Start, run, publish results, and stop.
A worker that should stay available or maintain a desired count ECS service ECS keeps the configured service tasks running.
Tests run on one machine while browsers live in a container elsewhere Playwright Server in a container Use Playwright’s documented remote connection model, with the server endpoint access-controlled.

This article focuses on an ECS task on Fargate. If you use Playwright Server, match the client Playwright version to the server image version. The server endpoint should not be exposed as an unauthenticated public service; choose an access-control design appropriate for your AWS network.

2. Create a small Playwright test project

The example uses Node.js and Chromium. Keep the project dependency and Docker image on the same Playwright release. Replace the example URL with a site you are authorized to test.

// package.json
{
  "name": "playwright-aws-task",
  "private": true,
  "scripts": { "test": "node test.js" },
  "dependencies": { "playwright": "1.52.0" }
}
// test.js
const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com', { waitUntil: 'domcontentloaded', timeout: 30000 });
    console.log(JSON.stringify({
      title: await page.title(),
      url: page.url()
    }));
    await page.screenshot({ path: '/tmp/example.png', fullPage: true });
  } finally {
    await browser.close();
  }
})().catch(error => {
  console.error(error);
  process.exitCode = 1;
});

For real test suites, use your normal Playwright test runner and ensure the process exits nonzero when assertions fail. Write artifacts to a known directory and export them before the task stops.

3. Build a reproducible Docker image

The official Playwright image includes browser binaries and browser system dependencies, but not the Playwright package for your project. Pin the image tag and the package to the same release. A mismatch can leave Playwright unable to find the browser executable it expects. Check the current Playwright Docker documentation when updating the version.

# Dockerfile
FROM mcr.microsoft.com/playwright:v1.52.0-noble
WORKDIR /app
COPY package.json ./
RUN npm install --omit=dev
COPY test.js ./
CMD ["npm", "test"]

For a production test image, commit a lockfile and use npm ci --omit=dev so dependency resolution is repeatable. If using @playwright/test, install it as a production dependency for this image, or use the appropriate build stage. Do not assume the base image installed your project package. Playwright’s [Docker guidance](https://playwright.dev/docs/next/docker) recommends pinning a specific image version; its [browser documentation](https://playwright.dev/docs/browsers) explains browser installation and version matching.

# Build and smoke-check locally
DOCKER_IMAGE=playwright-aws-task:1.52.0
docker build -t "$DOCKER_IMAGE" .
docker run --rm --init --ipc=host "$DOCKER_IMAGE"

For local Docker, Playwright recommends --init to handle process reaping and --ipc=host for Chromium because limited shared memory can cause crashes. These are local Docker options; Fargate does not support ipcMode or sharedMemorySize. Validate browser stability using the actual Fargate task memory and workload instead of copying the local IPC setting into the task definition.

4. Push the image to Amazon ECR

Set these shell variables for your AWS account and region. The commands assume the AWS CLI is configured with permission to create or push to the repository. Choose an AWS region and keep the ECR repository, ECS cluster, and task networking consistent with it.

export AWS_REGION=us-east-1
export AWS_ACCOUNT_ID=123456789012
export ECR_REPOSITORY=playwright-aws-task
export IMAGE_TAG=1.52.0

aws ecr create-repository \
  --repository-name "$ECR_REPOSITORY" \
  --region "$AWS_REGION" 2>/dev/null || true

aws ecr get-login-password --region "$AWS_REGION" |
  docker login --username AWS --password-stdin \
    "$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com"

export IMAGE_URI="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY:$IMAGE_TAG"
docker tag playwright-aws-task:1.52.0 "$IMAGE_URI"
docker push "$IMAGE_URI"

For repeatable deployments, use an immutable image tag or record the image digest deployed by the task definition. Updating the image should be an explicit build and deployment action, not an accidental change caused by a floating tag.

5. Set up ECS roles, logs, and networking

  1. Execution role: configure the ECS task execution role for ECS agent operations, such as pulling a private ECR image and delivering logs to CloudWatch Logs. This is distinct from permissions used by the test code.
  2. Task role: attach an application task role only if the test code calls AWS APIs, and grant only the actions and resources it needs. Do not put long-lived AWS credentials in the image or source code.
  3. Network: Fargate uses awsvpc networking. Place tasks in subnets with the required route for the target sites and AWS endpoints. Set security-group egress for required test traffic; restrict inbound traffic unless the task intentionally provides a service endpoint.
  4. Logs: use the awslogs log driver to send stdout and stderr to CloudWatch Logs. Set the log group and region in the container definition and ensure the execution role permits delivery.

AWS recommends separating task execution and task roles in its [ECS IAM role guidance](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/security-iam-roles.html). Review the [Fargate security considerations](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/fargate-security-considerations.html): tasks have dedicated infrastructure capacity and cannot access the underlying host, but containers in the same task share resources and a network namespace. A sidecar in the task is not an isolation boundary from the browser container.

6. Register a Fargate task definition

Save this as task-definition.json, replacing the account, region, execution role ARN, and log group as needed. The example sets task-level CPU and memory and uses a container command override for the test script. Fargate supports specific CPU and memory combinations; consult the current [task definition differences reference](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/fargate-tasks-services.html) before changing these values.

{
  "family": "playwright-batch",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "1024",
  "memory": "2048",
  "executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "name": "playwright",
      "image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/playwright-aws-task:1.52.0",
      "essential": true,
      "command": ["npm", "test"],
      "environment": [
        { "name": "NODE_ENV", "value": "production" }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/playwright-batch",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "task"
        }
      }
    }
  ]
}
aws logs create-log-group \
  --log-group-name /ecs/playwright-batch \
  --region "$AWS_REGION" 2>/dev/null || true

aws ecs register-task-definition \
  --cli-input-json file://task-definition.json \
  --region "$AWS_REGION"

Fargate task definitions must use awsvpc. They do not accept ipcMode, sharedMemorySize, or privileged containers. Some larger task sizes require platform version 1.4.0 or later. See AWS’s current [Fargate task reference](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/fargate-tasks-services.html) for supported combinations and constraints.

7. Run the task

Choose subnets that provide the task’s required outbound connectivity. For a public subnet, public IP assignment and routing must be configured appropriately. Private subnets need a route for required internet traffic or another supported egress path. The security group should permit the necessary outbound requests and normally allow no inbound connections for a batch task.

export ECS_CLUSTER=playwright-cluster
export SUBNET_ID=subnet-0123456789abcdef0
export SECURITY_GROUP_ID=sg-0123456789abcdef0

aws ecs run-task \
  --cluster "$ECS_CLUSTER" \
  --launch-type FARGATE \
  --platform-version LATEST \
  --task-definition playwright-batch \
  --network-configuration "awsvpcConfiguration={subnets=[$SUBNET_ID],securityGroups=[$SECURITY_GROUP_ID],assignPublicIp=ENABLED}" \
  --region "$AWS_REGION"

Use assignPublicIp=DISABLED when your subnet routing and egress design provides the required connectivity without a public task IP. Fargate platform versions are revised over time; AWS documents that new tasks use the latest revision of the selected platform version, and LATEST is used when no version is specified for a service. Include platform-version decisions in deployment maintenance. See [Fargate platform versions](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/platform-fargate.html).

8. Capture and export test artifacts

A task’s filesystem is temporary. Save screenshots, traces, downloads, and reports to a predictable directory, then upload them to durable storage or another artifact destination before the task exits. For example, an application can write under /tmp/artifacts and upload files to S3 using a narrowly scoped task role. Keep secrets and test artifacts out of logs unless they are safe to expose.

Linux Fargate tasks on platform 1.4.0 or later have at least 20 GiB of ephemeral storage by default, configurable up to 200 GiB. Compressed and uncompressed image layers consume part of that allocation. Estimate scratch space for browser profiles, downloads, screenshots, and traces in addition to the image footprint. See [Fargate task ephemeral storage](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/fargate-task-storage.html).

9. Security guidance for browser workloads

Playwright’s Docker documentation states: “This Docker image is intended for testing and development purposes only. It is not recommended to use this Docker image to visit untrusted websites.” For untrusted crawling or scraping, Playwright’s guidance describes running with a separate user and a seccomp profile. Running Chromium as root disables its sandbox. Review the [official Docker guidance](https://playwright.dev/docs/next/docker) before designing an untrusted browsing workload.

  • Keep test credentials in an appropriate secrets system and inject only what the task needs.
  • Scope task-role permissions to the APIs and resources used by the test code.
  • Restrict outbound network access when the job does not need unrestricted destinations.
  • Separate workloads with different trust levels into distinct tasks and roles.
  • Do not treat Fargate’s host isolation or a sidecar as a replacement for browser-level security and egress controls.

Fargate does not support privileged containers and limits capabilities. These platform constraints are useful, but they do not replace careful IAM, network, secret, and workload design.

10. Size and tune the task from measurements

There is no universal CPU, memory, or browser concurrency setting for Playwright. Begin with a conservative task size, run representative tests, and observe task memory, CPU, duration, failures, and scratch-space use. Increase resources or reduce parallel browser contexts when the observed workload needs it. Test the same image and task configuration you plan to deploy.

  • Memory: account for the browser process, pages, video or trace collection, and the Node.js process. A crash under load may be resource pressure, but confirm using task and application logs.
  • Concurrency: more simultaneous pages or workers can raise CPU and memory needs and increase load on target sites. Tune against the actual suite.
  • Startup time: image size and browser initialization affect how quickly a finite task gets to work. Keep dependencies intentional and use a stable image tag.
  • Storage: include browser caches, downloads, traces, and reports in the scratch-space estimate. Export artifacts before task termination.

AWS’s Fargate CPU and memory table defines permitted configurations, not Playwright performance recommendations. Measure on the target region and workload. The research sources do not provide a cost comparison among Fargate, ECS on EC2, and other compute options, so compare current regional prices and operational needs for your own job.

11. Troubleshooting

Symptom Likely cause What to check or change
“Executable doesn’t exist” or browser launch cannot find Chromium Playwright package and image browser versions do not match, or the browser was not installed. Pin the same Playwright release in the image tag and package dependency. If using a custom base image, install browsers and system dependencies with the Playwright CLI.
Image pull fails with authorization errors Execution role, repository access, region, or image URI is wrong. Check the ECR image URI and region, and ensure the task execution role can pull from the private repository.
Task starts but cannot reach the target URL Subnet route, DNS, security-group egress, or target-side policy prevents access. Check the task’s VPC route, DNS, outbound rules, and any proxy or destination allowlist required by your environment.
Task stops before logs appear Log group, region, log driver options, or execution-role permissions are incorrect. Confirm the log group exists in the configured region, the container uses awslogs, and the execution role can deliver logs.
Chromium crashes under load Memory pressure, too many concurrent pages, or shared-memory assumptions carried over from local Docker. Inspect task resource use and logs, reduce concurrency or increase task memory within supported combinations, and validate on Fargate. Do not configure unsupported IPC settings.
Test passes locally but fails in Fargate Different network access, environment variables, architecture, browser setup, or timing. Run the exact pushed image locally, compare environment and routes, and make waits depend on the page condition rather than arbitrary timing where possible.
Artifacts disappear after the task exits Files were saved only to the task’s ephemeral filesystem. Upload them to durable storage before exit and verify the upload in the task’s result path.
Task cannot use --ipc=host or privileged settings Those local Docker settings are not supported by Fargate task definitions. Remove them from the Fargate configuration; tune supported task CPU and memory and confirm browser behavior in the target environment.

12. Or skip the browser setup

If the job is simply to capture a webpage image or PDF, [ScreenshotNeo](https://screenshotneo.com) provides a website screenshot API and MCP server. A single GET request returns a PNG, JPEG, WebP, or PDF. The API parameters used by other screenshot APIs also work, which can make switching easier. See the [ScreenshotNeo API documentation](https://screenshotneo.com/docs/).

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the capture; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. [Create a free ScreenshotNeo account](https://screenshotneo.com/account/sign-up/).

Frequently asked questions

Can I use the official Playwright image as a complete application image?

It provides browser binaries and system dependencies, but you still install the Playwright package your application uses.

Should I run browser automation as an ECS service?

Only when a continuously available worker or maintained desired task count fits the workload. Finite test batches can run as ECS tasks.

Does Fargate support the same Docker flags as my laptop?

No. In particular, Fargate task definitions do not accept ipcMode or sharedMemorySize, and privileged containers are not supported.

Is Fargate always the lowest-cost option?

The cited documentation does not establish that. Compare current regional pricing and operational requirements for the compute choices you are considering.

Where should I start when upgrading Playwright?

Update the pinned package and matching image version together, rebuild the image, and run the test suite against the new browser set before deployment.