A minimal Browserless-like service that allows users to programmatically create and manage headless browser instances on demand.
- Launch headless Chromium browsers via REST API
- Get WebSocket URLs for Playwright/Puppeteer connections
- List all browser instances with status and connection info
- Automatic lifecycle management (idle timeout, expiration, cleanup)
- Health monitoring and restart capabilities
- Python 3.10+
- Docker (for PostgreSQL)
- uv (Python package manager)
# Install uv if not already installed
curl -LsSf https://astral.sh/uv/install.sh | shdocker compose up -d dbcd backend
uv syncuv run playwright install chromiumuv run alembic upgrade headuv run fastapi dev app/main.pyThe API will be available at http://localhost:8000
cd backend
uv run python scripts/test_browser_service.pyThis script will:
- Create a browser instance via the API
- Connect to it using Playwright's CDP connection
- Run automation demos (navigate, screenshot, form fill)
- Save screenshots to
/tmp/browser_service_screenshots/ - Terminate the browser
============================================================
π Browser-as-a-Service Demo
============================================================
π¦ Creating browser instance...
β
Browser created: 089d52fe-a976-4199-aeb7-c723e95f820e
Status: running
CDP Port: 36123
WebSocket URL: ws://127.0.0.1:36123
π Connecting to browser via CDP...
β
Connected to browser via CDP!
π Demo 1: Navigate to example.com
Title: Example Domain
πΈ Screenshot: /tmp/browser_service_screenshots/01_example_com.png
...
β¨ Demo completed!
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/v1/browsers/ |
Create a new browser instance |
GET |
/api/v1/browsers/ |
List all browser instances |
GET |
/api/v1/browsers/{id} |
Get browser details |
DELETE |
/api/v1/browsers/{id} |
Terminate a browser |
POST |
/api/v1/browsers/{id}/keepalive |
Reset idle timeout |
POST |
/api/v1/browsers/{id}/restart |
Restart stopped browser |
WS |
/api/v1/browsers/{id}/ws |
WebSocket proxy for CDP |
curl -X POST http://localhost:8000/api/v1/browsers/ \
-H "Content-Type: application/json" \
-d '{
"idle_timeout_seconds": 300,
"max_lifetime_seconds": 3600
}'Response:
{
"id": "089d52fe-a976-4199-aeb7-c723e95f820e",
"status": "running",
"websocket_url": "ws://127.0.0.1:36123",
"cdp_port": 36123,
"idle_timeout_seconds": 300,
"max_lifetime_seconds": 3600
}from playwright.async_api import async_playwright
async def connect_to_browser(cdp_port: int):
async with async_playwright() as p:
browser = await p.chromium.connect_over_cdp(f"http://127.0.0.1:{cdp_port}")
page = await browser.new_page()
await page.goto("https://example.com")
await page.screenshot(path="screenshot.png")
await browser.close()const { chromium } = require('playwright');
async function connectToBrowser(cdpPort) {
const browser = await chromium.connectOverCDP(`http://127.0.0.1:${cdpPort}`);
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'screenshot.png' });
await browser.close();
}curl http://localhost:8000/api/v1/browsers/curl -X DELETE http://localhost:8000/api/v1/browsers/{browser_id}cd backend
uv run pytest tests/api/routes/test_browsers.py tests/api/routes/test_ws.py -vbackend/
βββ app/
β βββ api/routes/
β β βββ browsers.py # Browser CRUD endpoints
β β βββ ws.py # WebSocket proxy endpoint
β βββ services/
β β βββ browser_manager.py # Playwright browser lifecycle
β β βββ ws_proxy.py # CDP WebSocket proxy
β β βββ scheduler.py # Background task scheduler
β βββ tasks/
β β βββ cleanup.py # Idle/expiry cleanup tasks
β βββ models.py # Database models
β βββ main.py # FastAPI app entry point
βββ scripts/
β βββ test_browser_service.py # Demo script
βββ tests/
βββ api/routes/
βββ test_browsers.py
βββ test_ws.py
Environment variables (set in .env):
| Variable | Default | Description |
|---|---|---|
BROWSER_MAX_CONCURRENT |
10 | Max concurrent browser instances |
BROWSER_DEFAULT_IDLE_TIMEOUT |
300 | Default idle timeout (seconds) |
BROWSER_DEFAULT_MAX_LIFETIME |
3600 | Default max lifetime (seconds) |
| Status | Description |
|---|---|
pending |
Created but not yet started |
running |
Browser is active and accepting connections |
sleeping |
Idle timeout reached, process stopped |
stopped |
Gracefully terminated |
error |
Failed to start or crashed |
- FastAPI - Web framework
- PostgreSQL - Database
- SQLModel - ORM
- Playwright - Browser automation
- APScheduler - Background tasks
- uv - Package manager