Skip to content

Playwright

Description / nameInput element
Container Registry

Build Status Last Commit

Playwright (Chromium) on FreeBSD. Use as a base image for running browser tests.

Registry ghcr.io/daemonless/playwright
Daemonless daemonless/playwright
Source microsoft/playwright
Website playwright.dev

Version Tags

Tag Description Best For
latest Upstream Binary. Built from official release. Most users. Matches Linux Docker behavior.
Podman on FreeBSD currently requires root. All commands must be run as root (or via doas/sudo).

Before deploying, ensure your host environment is ready. See the Quick Start Guide for host setup instructions.

Deployment

podman run --rm \
  ghcr.io/daemonless/playwright:latest

This image is intended as a base image for running browser automation scripts, not as a long-running service.

Write a script using playwright-core and run it with podman run --rm:

// test.js
const { chromium } = require('playwright-core');

(async () => {
  const browser = await chromium.launch({
    args: ['--no-sandbox', '--disable-setuid-sandbox'],
  });
  const context = await browser.newContext({
    recordVideo: { dir: '/app/videos/' },
    ignoreHTTPSErrors: true,
  });
  const page = await context.newPage();

  try {
    await page.goto('https://example.com');
    // ... your test logic ...
  } finally {
    await context.close();
    await browser.close();
  }
})();
podman run --rm \
  -v /path/to/test.js:/app/test.js:ro \
  -v /path/to/videos:/app/videos \
  --network host \
  ghcr.io/daemonless/playwright:latest \
  node /app/test.js

Videos are recorded as .webm files. Convert to mp4 with:

ffmpeg -i /path/to/videos/*.webm output.mp4

Real-World Example

Login to a web app and record a video:

// login.js
const { chromium } = require('playwright-core');

(async () => {
  const browser = await chromium.launch({
    args: ['--no-sandbox', '--disable-setuid-sandbox'],
  });
  const context = await browser.newContext({
    recordVideo: { dir: '/app/videos/', size: { width: 1280, height: 720 } },
    ignoreHTTPSErrors: true,
  });
  const page = await context.newPage();

  try {
    await page.goto('https://myapp:8181');
    await page.waitForLoadState('networkidle');

    await page.locator('input[placeholder="Enter your username"]').fill('admin');
    await page.locator('input[type="password"]').fill('admin');

    await Promise.all([
      page.waitForLoadState('networkidle'),
      page.getByRole('button', { name: 'Login' }).click(),
    ]);

    // Wait for a post-login element instead of a fixed sleep
    await page.waitForTimeout(3000);
  } finally {
    await context.close();
    await browser.close();
  }
})();
podman run --rm \
  -v /path/to/login.js:/app/test.js:ro \
  -v /path/to/videos:/app/videos \
  --network host \
  ghcr.io/daemonless/playwright:latest \
  node /app/test.js

FreeBSD Notes

Chromium and ffmpeg are installed from FreeBSD packages and symlinked into the paths playwright-core expects. The playwright-core npm package is patched at build time to register freebsd-amd64 as a native platform — no Linux masquerade needed.

Manual Install (without container)

git clone https://github.com/daemonless/playwright
pkg install node22 npm-node22 chromium ffmpeg
npm install -g playwright-core
sh playwright/root/build/patch-playwright.sh
doas sh playwright/root/build/setup-ms-playwright.sh

Add to your shell profile:

export PLAYWRIGHT_BROWSERS_PATH=/usr/local/libexec/ms-playwright
export PLAYWRIGHT_HOST_PLATFORM_OVERRIDE=freebsd-amd64
export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
export NODE_PATH=$(npm root -g)

Implementation Details

  • Architectures: amd64
  • User: bsd (UID/GID set via PUID/PGID). Defaults to 1000:1000.
  • Base: Built on ghcr.io/daemonless/base (FreeBSD 15.0).

Need help? Join our Discord community.