A .NET Aspire hosting integration for Microsoft Playwright, allowing you to easily add a Playwright Docker container to your distributed application.
This integration provides an easy way to run Playwright in a Docker container as part of your .NET Aspire application. It's based on the official Microsoft Playwright Docker image and includes support for remote browser automation, making it ideal for end-to-end testing and web scraping scenarios.
- 🐳 Docker-based: Uses the official
mcr.microsoft.com/playwright/dotnetDocker image - 🔗 WebSocket Connection: Exposes Playwright server on port 3000 with WebSocket endpoint
- 💾 Persistent Storage: Optional volume mounting for browser cache and data
- 🔧 Development Mode: Support for development environments with additional capabilities
- 🌐 Host Network Access: Optional access to host machine services
- ⚡ Container Management: Proper Docker container lifecycle management
- 🛡️ Security: Runs with non-root user by default (pwuser)
Add a reference to the Playwright hosting project in your AppHost:
<ProjectReference Include="../Aspire.Hosting.Playwright/Aspire.Hosting.Playwright.csproj" IsAspireProjectResource="false" />using Aspire.Hosting.Playwright;
var builder = DistributedApplication.CreateBuilder(args);
// Basic Playwright setup
var playwright = builder.AddPlaywright("playwright");
// Advanced setup with data volume and host network access
var playwrightAdvanced = builder.AddPlaywright("playwright-advanced")
.WithDataVolume() // Persistent browser cache
.WithHostNetworkAccess(); // Access to host services
builder.Build().Run();using Microsoft.Playwright;
// Get the connection string from configuration
var playwrightUrl = builder.Configuration.GetConnectionString("playwright");
// Connect to the remote Playwright server
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.ConnectAsync(playwrightUrl);
var page = await browser.NewPageAsync();
await page.GotoAsync("https://example.com");// Simple setup with default configuration
var playwright = builder.AddPlaywright("playwright");
// Custom port
var playwright = builder.AddPlaywright("playwright", port: 3001);// Add a data volume for browser cache and downloads
var playwright = builder.AddPlaywright("playwright")
.WithDataVolume();
// Custom volume name
var playwright = builder.AddPlaywright("playwright")
.WithDataVolume("my-playwright-data");// Enable development mode (adds SYS_ADMIN capability)
// Only use in development environments
var playwright = builder.AddPlaywright("playwright")
.WithDevelopmentMode();// Allow Playwright container to access host services
// Services running on host can be accessed via 'hostmachine' hostname
var playwright = builder.AddPlaywright("playwright")
.WithHostNetworkAccess();The integration automatically configures the Playwright container with:
- Image:
mcr.microsoft.com/playwright/dotnet:v1.53.0-noble - Port: 3000 (WebSocket server)
- User:
pwuser(non-root for security) - Working Directory:
/home/pwuser - Runtime Args:
--init --ipc=hostfor proper process handling and shared memory - Environment Variables:
PLAYWRIGHT_BROWSERS_PATH=/ms-playwrightPLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
The Playwright container doesn't expose a standard HTTP health check endpoint since it runs a WebSocket server. The container is considered ready when it starts successfully and begins listening on port 3000. You can verify connectivity by attempting to connect via the WebSocket endpoint.
- Default User: Runs as
pwuser(non-root) for better security - Development Mode: Only use
WithDevelopmentMode()in development environments as it adds elevated privileges - Host Access: Be cautious with
WithHostNetworkAccess()in production environments
The current integration uses Playwright v1.53.0 with the following browsers:
- Chromium
- Firefox
- WebKit (Safari)
All browsers are pre-installed in the Docker image.
var builder = DistributedApplication.CreateBuilder(args);
var apiService = builder.AddProject<Projects.MyApi>("api");
var webApp = builder.AddProject<Projects.MyWebApp>("webapp")
.WithReference(apiService);
// Playwright for E2E testing with access to local services
var playwright = builder.AddPlaywright("playwright")
.WithHostNetworkAccess() // Access webapp and api via 'hostmachine'
.WithDataVolume(); // Persist browser cache
builder.Build().Run();var playwright = builder.AddPlaywright("playwright")
.WithDataVolume("scraper-cache") // Persistent cache for scraping sessions
.WithDevelopmentMode(); // For development only
// In your scraping service
var page = await browser.NewPageAsync();
await page.GotoAsync("https://example.com");
var content = await page.InnerTextAsync("body");- Connection Refused: Ensure the container is fully started before connecting
- Browser Crashes: Try using
WithDevelopmentMode()for development environments - Memory Issues: The
--ipc=hostflag is automatically added to prevent shared memory issues - Host Access: Use
hostmachineinstead oflocalhostwhen accessing host services
Check the container logs in the Aspire dashboard for detailed error information.
Feel free to contribute to this integration by submitting issues or pull requests.
This project follows the same license as .NET Aspire.