Playwright Docker Image: Package Your QA Agent

“It works on my machine” stops being a problem when your machine travels with the code. A Playwright Docker image packages your agent with the exact browsers and system libraries that it needs. In this lesson you will write a container image that is pinned, runs as a normal user and contains no secrets.

📖 What You’ll Learn

  • Why containers suit browser testing
  • How to pin the Playwright image
  • How to run as a non-root user
  • How to keep secrets out of an image

💡 Why Use the Playwright Docker Image?

Explain Like I’m 5: A Packed Lunchbox

Imagine: A lunchbox holds everything you need, so it does not matter which table you sit at.

What it really means: A container carries the app and everything that it depends on.

In testing: Browsers and system libraries always match your Playwright version.

The official image already includes the browsers. If you install Docker Desktop or Docker Engine, you can build and run it anywhere.

Match the versions exactly

The image tag must match the @playwright/test version in your package.json. If they differ, Playwright cannot find its browser files.


🔍 Write the Dockerfile

Explain Like I’m 5: Labelling the Jar

Imagine: A jar marked “jam” could hold anything. A jar marked “strawberry, June” tells you exactly what is inside.

What it really means: A pinned tag says exactly which version you are using.

In testing: Never use latest for a production image.

Create Dockerfile.

# Pin this to the same version as @playwright/test in package.json.
FROM mcr.microsoft.com/playwright:v1.65.0-noble

WORKDIR /app

COPY package.json package-lock.json ./
RUN npm ci

COPY . .
RUN npm run typecheck

# The app runs as a normal user that can write only to these folders.
RUN mkdir -p data artifacts reports playwright-report tests/generated \
  && chown -R pwuser:pwuser data artifacts reports playwright-report tests/generated

USER pwuser
ENV NODE_ENV=production
ENV SERVICE_HOST=0.0.0.0
EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
  CMD node -e "fetch('http://127.0.0.1:8080/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"

# Runs the demo target beside the service. Lesson 82 explains how to remove it.
CMD ["sh", "-c", "npx tsx demo/server.ts & exec npx tsx src/service/main.ts"]
LineWhy it is there
Pinned FROM tagThe same browsers on every build
COPY package*.json firstDependencies are cached between builds
RUN npm run typecheckA broken build cannot be shipped
USER pwuserThe app does not run as root
Limited chownThe app can write only to named folders
HEALTHCHECKThe platform can see when it is unhealthy

🧩 Keep Secrets Out

Explain Like I’m 5: Writing a Password on the Lunchbox

Imagine: Anyone who sees the box can read it, wherever it goes.

What it really means: Anything copied into an image travels with it.

In testing: The .env file is never included in the build.

Create .dockerignore.

node_modules
.git
.env
data
artifacts
reports
playwright-report
test-results
tests/generated

Secrets are given to the container when it starts, as you will see in Deploy an AI Agent to the Cloud. They should never be in the image, because anyone who can pull the image could read them.


🛠️ Build and Run It Locally

These commands need Docker to be installed. Build first, and read any errors carefully.

docker build -t qa-agent:local .

Run the container with the recommended options.

docker run --rm --init -p 127.0.0.1:8080:8080 --env-file .env -e AUTH_MODE=token qa-agent:local

The --init option helps to clean up browser child processes. Check it from another terminal.

curl http://127.0.0.1:8080/healthz

Your first build may reveal problems that a laptop hides, such as a file that was never committed. That is one of the benefits of building a container.


📌 Running Chromium Safely

The Playwright documentation advises against using this image to visit untrusted websites. That matches your project’s rule of testing only approved environments. For stronger browser isolation, the documentation describes running as pwuser together with a custom seccomp profile.

You can also review Playwright GitHub Actions to see the same browser setup in a pipeline.


🌍 Real-Life Example: The Missing Library

A tester’s agent runs perfectly on Windows. On a Linux server, Chromium fails with an error about a missing shared library. Installing system packages by hand becomes a long afternoon. The official image includes those libraries, so the problem never appears.


🧪 Try It Yourself

  1. Create both files.
  2. Confirm that the image tag matches your package.json.
  3. If you have Docker, build the image and run it.
  4. Run docker history and look for anything sensitive.
  5. Run whoami inside the container, and confirm that it is pwuser.

📋 Ready-to-Copy Prompt

You are a container security reviewer.

My Dockerfile:
[paste it]

My .dockerignore:
[paste it]

Check for: unpinned images, root users, secrets in layers, missing health
checks and folders that are writable when they should not be.

Return a table with: Line, Risk and Smallest Fix. Tell me to confirm the
current image tag on the official registry.

⚠️ Common Mistakes

  • Using the latest tag
  • Letting the image and package versions differ
  • Copying .env into the image
  • Running the service as root
  • Testing only on the developer’s machine

🎯 Key Takeaways

  • A container carries the browsers and libraries with it.
  • Pin the image to your exact Playwright version.
  • Run as a normal user with limited write access.
  • Pass secrets when the container starts.

🎤 Interview Questions

1. Why run Playwright in Docker?

Model answer: It provides the same browsers and system libraries everywhere, which avoids environment differences between machines and CI.

2. Why pin the Playwright image version?

Model answer: The tests and browser builds must match. A pinned tag also makes builds repeatable and protects against unexpected changes.

3. How do you keep secrets out of a Docker image?

Model answer: I exclude them with .dockerignore and provide them at runtime through the platform’s secret mechanism. Image layers can be read by anyone who pulls them.

4. Why not run as root in a container?

Model answer: If the app is compromised, a non-root user limits the damage. I also limit which folders the user can write to.

5. What does a health check do?

Model answer: It lets the platform detect an unhealthy container and restart or replace it.


📝 Assignment

Build your image and record its size. Add a second Dockerfile stage if you can make the image smaller safely. Then check it with a container scanner, and fix one finding.


❓ FAQ

Do I need Docker to complete this course?

No. It is needed only if you deploy with containers. Read the lesson and come back to it later if necessary.

Why does the image include the demo app?

It lets you prove the deployment without touching any other system. The next lesson shows how to point it at your own test environment.


🚀 Next Steps

Well done on finishing this lesson. Complete the assignment, then continue with Deploy an AI Agent to the Cloud: A Safe First Release.