Playwright
The Playwright module runs your Playwright browser tests in a container whose browsers always match your Playwright version — the same way locally, in CI, and in Dagger Cloud. Its distinctive feature is first-class module wiring: if another module in your workspace serves your app, one line of configuration points the tests at it. No glue module, no port juggling.
Official module: dagger/playwright
Add it to your workspace
dagger install github.com/dagger/playwright
Pin your project's @playwright/test version exactly (or commit a lockfile).
The module runs your tests in the mcr.microsoft.com/playwright image
matching the version installed per package-lock.json — or, without a
lockfile, the version declared in package.json, where a floating range like
^1.58.2 can install a newer Playwright than the image's browsers.
Run the check
dagger check # run every check in the workspace
dagger check playwright:test # just the Playwright suite
playwright:test finds the directory containing playwright.config.*,
installs your project's dependencies, and runs npx playwright test. If your
config declares a webServer,
Playwright starts your app inside the container exactly as it does on your
machine — no further setup needed.
Wire in the service under test
If another module already serves your app, wire it into the tests instead of
duplicating that knowledge in webServer. Two steps:
1. Set the service setting to a module reference — the install name of
another module in your dagger.toml, and a function on it that returns a
Service. Run dagger up -l to list the candidates in copyable form:
[modules.playwright.settings]
service = "myapp:serve"
2. Add PLAYWRIGHT_BASE_URL to your playwright.config. The module
binds the service into the test container and communicates its address
through this environment variable — if your config doesn't read it, your
tests will ignore the wired service and keep targeting whatever baseURL
hardcodes:
use: {
baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:3000',
},
Keep your local URL as the fallback so the same config works on your machine.
By default the service is bound as frontend; set serviceHostname if your
tests need a different hostname.
Secure contexts (service workers, WebCrypto, PWA testing)
Browser APIs that require a secure context don't work against
http://frontend:<port> — only localhost or HTTPS origins qualify. If any
of your tests exercise service workers, WebCrypto, or other secure-context
APIs, enable the localhost proxy:
[modules.playwright.settings]
service = "myapp:serve"
localhostProxy = true
Then point those tests at PLAYWRIGHT_LOCALHOST_BASE_URL, which the proxy
sets — for example as the baseURL of a dedicated project in your config:
{
name: 'chromium-pwa',
use: {
...devices['Desktop Chrome'],
baseURL: process.env.PLAYWRIGHT_LOCALHOST_BASE_URL || 'http://localhost:3000',
},
},
Prepare your config for the container
The module sets CI=true, so review what your playwright.config keys off
process.env.CI — retries, forbidOnly, and especially workers:
- Replace a
workers: process.env.CI ? 1 : undefinedclamp with a bounded value like4. The container is isolated, so the usual shared-CI reason to serialize doesn't apply — a1clamp can make the suite many times slower. Don't go unbounded either: too many workers starve the browsers and blow test timeouts. - Remove branded-browser projects (
channel: 'msedge',channel: 'chrome') or exclude them with theargssetting. Those browsers are not present in the Playwright images; the bundledchromiumcovers the same engine.
Configure it
List the current settings with dagger settings playwright, then change one
with dagger settings playwright <key> <value>. They live in dagger.toml
under [modules.playwright.settings]:
sourcePath(default: discover): workspace path of the Playwright project. Set it when the workspace holds more than oneplaywright.config.*.service: module reference ("module:function") of the service under test.serviceHostname(defaultfrontend): hostname the service is bound as inside the test container.baseImageAddress(default: derive): set this to override the derivedmcr.microsoft.com/playwright:v<version>-nobleimage.baseCtr: a fullContaineroverride — also wireable, e.g.baseCtr = "base-images:chromium".packageManager(defaultnpm): set toyarn,pnpm, orbunto match your project.localhostProxy(defaultfalse): see secure contexts above.args(default[]): extraplaywright testarguments, e.g.["--project", "chromium"].shards(default1): set to shard the check across parallel containers. Shards run concurrently against the same wired service and fail fast on the first failure.
[modules.playwright.settings]
service = "myapp:serve"
shards = 4
Get the HTML report
To inspect a failing run, call report: it runs the suite tolerating
failures and returns the HTML report directory. Include the html reporter
in your config, then export the report to your machine:
dagger api call playwright report -o ./playwright-report
Working with other modules
Playwright covers browser-level end-to-end testing; pair it with
Jest or Vitest for unit tests. Any module whose
function returns a Service can be the app under test — that's the
wiring contract, not a special
integration.