level 08 / playwright-in-docker
Playwright in Docker
Run Playwright tests reliably in Docker containers using the official Microsoft image.
The Official Playwright Docker Image
Microsoft publishes official images at mcr.microsoft.com/playwright:
# Pull specific version (always pin in CI)
docker pull mcr.microsoft.com/playwright:v1.45.0-jammy
# Available variants
mcr.microsoft.com/playwright:v1.45.0-jammy # Ubuntu 22.04 (recommended)
mcr.microsoft.com/playwright:v1.45.0-focal # Ubuntu 20.04
mcr.microsoft.com/playwright:v1.45.0-jammy-arm64 # Apple Silicon / ARM CI
Always pin the version tag — latest will silently update and break reproducibility.
Dockerfile for Playwright Project
FROM mcr.microsoft.com/playwright:v1.45.0-jammy
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY playwright.config.ts .
COPY e2e/ ./e2e/
COPY src/ ./src/
ENV CI=true
CMD ["npx", "playwright", "test"]
Running Tests in Docker
# Basic run
docker run --rm \
mcr.microsoft.com/playwright:v1.45.0-jammy \
bash -c "cd /app && npx playwright test"
# With test project mounted (live code, no rebuild)
docker run --rm \
-v $(pwd):/app \
-w /app \
-e BASE_URL=https://staging.example.com \
mcr.microsoft.com/playwright:v1.45.0-jammy \
npx playwright test
# Collect report
docker run --rm \
-v $(pwd):/app \
-w /app \
-v $(pwd)/playwright-report:/app/playwright-report \
mcr.microsoft.com/playwright:v1.45.0-jammy \
npx playwright test --reporter=html
Headed Mode in Docker (with Xvfb)
The official image includes Xvfb for headed testing:
# Run with virtual display
docker run --rm \
-e DISPLAY=:99 \
mcr.microsoft.com/playwright:v1.45.0-jammy \
bash -c "Xvfb :99 -screen 0 1280x1024x24 & npx playwright test --headed"
Generally unnecessary — use headless in CI. Headed Docker is for debugging specific rendering issues.
Security: Non-Root User
FROM mcr.microsoft.com/playwright:v1.45.0-jammy
WORKDIR /app
# Create non-root user
RUN groupadd -r pwuser && useradd -r -g pwuser -G audio,video pwuser \
&& mkdir -p /home/pwuser/Downloads \
&& chown -R pwuser:pwuser /home/pwuser \
&& chown -R pwuser:pwuser /app
COPY --chown=pwuser:pwuser package*.json ./
RUN npm ci
COPY --chown=pwuser:pwuser . .
USER pwuser
CMD ["npx", "playwright", "test"]
The official image already runs as a non-root user by default — check with docker run --rm mcr.microsoft.com/playwright:v1.45.0-jammy whoami.
Resource Limits
# Limit CPU and memory — prevent runaway containers in shared CI
docker run --rm \
--cpus="2.0" \
--memory="4g" \
--memory-swap="4g" \
my-pw-tests
Playwright + Chromium: budget ~500MB per worker. 4 workers = ~2GB.
Playwright Config Inside Docker
// playwright.config.ts — CI / Docker aware
export default defineConfig({
workers: process.env.CI ? 2 : undefined,
retries: process.env.CI ? 1 : 0,
use: {
headless: true, // always headless in container
launchOptions: {
args: [
'--no-sandbox', // required in some Docker environments
'--disable-setuid-sandbox',
'--disable-dev-shm-usage', // use /tmp instead of /dev/shm (small in Docker)
],
},
},
});
Note: The official Playwright image configures these flags automatically. Only add manually if using a custom base image.
CI Integration Example (GitHub Actions)
- name: Run Playwright in Docker
run: |
docker run --rm \
-v ${{ github.workspace }}:/app \
-w /app \
-e BASE_URL=${{ vars.STAGING_URL }} \
-e CI=true \
mcr.microsoft.com/playwright:v1.45.0-jammy \
npx playwright test --reporter=blob
# Then upload the blob artifact for report merging