Playwright (Chromium) on FreeBSD. Use as a base image for running browser tests.
| Registry | ghcr.io/daemonless/playwright |
| Source | https://github.com/microsoft/playwright |
| Website | https://playwright.dev/ |
| Tag | Description | Best For |
|---|---|---|
latest |
Upstream Binary. Built from official release. | Most users — recommended. |
podman run --rm \ ghcr.io/daemonless/playwright:latest
Save as run.sh, then run sh run.sh.
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.mp4Login 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
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.
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)
Architectures: amd64
User: bsd (UID/GID via PUID/PGID, defaults to 1000:1000)
Base: FreeBSD 15
Need help? Join our Discord community.