Playwright
| Description / name | Input element |
|---|---|
| Container Registry |
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
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:
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 to1000:1000. - Base: Built on
ghcr.io/daemonless/base(FreeBSD 15.0).
Need help? Join our Discord community.