A persistent Chrome instance that AI agents control via Playwright MCP. Log in once, delegate all web tasks to AI. Stop using your browser.
You run Chrome with a persistent profile. It stays logged into your accounts: Google, GitHub, LinkedIn, whatever you use. AI coding agents (Claude Code, Cursor, Windsurf, or anything that supports MCP) connect to it through Playwright and can navigate pages, click buttons, fill forms, read content.
You describe what you want in natural language. The agent does it.
"Check my open PRs for new comments" -> done
"Find hotels in Lisbon, March 15-18, max 120 EUR" -> shortlist with prices
"Go to LinkedIn and summarize my notifications" -> done
| Local | Remote | |
|---|---|---|
| Where Chrome runs | Your machine (headless) | A Linux server |
| Setup | 3 steps, 2 minutes | 7 steps, 10 minutes |
| Needs a server? | No | Yes (any VPS with 2GB+ RAM) |
| Login method | Use Chrome normally, then switch to headless | VNC one-time login |
| Best for | Trying it out, personal use | Always-on, shared across devices |
Both modes use the same persistent Chrome profile and CDP protocol. Your agent doesn't know or care which mode you're using.
No server needed. Chrome runs headless on your machine.
git clone https://github.com/buildingopen/openbrowser.git
cd openbrowser
./scripts/install-local.shThis detects your OS, finds Chrome, and starts it as a background service with CDP on localhost:9222.
Add to your Claude Code config (~/.claude.json):
{
"mcpServers": {
"openbrowser": {
"type": "stdio",
"command": "npx",
"args": ["@playwright/mcp@latest", "--cdp-endpoint=http://localhost:9222"]
}
}
}For Cursor, add to .cursor/mcp.json. For other MCP clients, consult their docs.
curl http://localhost:9222/json/version
# In Claude Code: "Navigate to github.com and tell me what you see"In headless mode there's no visible browser window. To log into your accounts:
./scripts/login-local.shThis stops the headless service, opens Chrome visually so you can log in, then restarts the service when you close Chrome.
Chrome runs on a Linux server. You connect through an SSH tunnel.
Your Machine (Claude Code / Cursor / any MCP client)
|-- Playwright MCP server
|-- SSH tunnel (autossh, auto-reconnects)
|-- Remote Server
|-- Xvfb (virtual display)
|-- Chrome (persistent profile, CDP on localhost:9222)
|-- Logged into your accounts
curl -sL https://raw.githubusercontent.com/buildingopen/openbrowser/main/scripts/install-server.sh | sudo bashOr from a clone:
git clone https://github.com/buildingopen/openbrowser.git /opt/openbrowser
sudo /opt/openbrowser/scripts/install-server.shThis installs Chrome, Xvfb, x11vnc, deploys the systemd service, and sets up health check and backup cron jobs. Requires an x86_64 server (Google does not publish Chrome for ARM64 Linux).
/opt/openbrowser/scripts/vnc-login.shFrom your local machine:
ssh -L 5900:localhost:5900 your-server
# Open vnc://localhost:5900 in any VNC client
# Log into Google (gives you SSO into GitHub, LinkedIn, etc.)
# Close VNC when done - the session auto-terminates./scripts/setup-tunnel.sh your-serverThis installs autossh as a system service (launchd on macOS, systemd on Linux) that auto-reconnects.
Or manually:
autossh -M 0 -N -L 9222:localhost:9222 your-serverSame as local mode:
{
"mcpServers": {
"openbrowser": {
"type": "stdio",
"command": "npx",
"args": ["@playwright/mcp@latest", "--cdp-endpoint=http://localhost:9222"]
}
}
}curl http://localhost:9222/json/version
# In Claude Code: "Navigate to github.com and tell me what you see"Run Chrome headless in a container with a persistent profile volume.
git clone https://github.com/buildingopen/openbrowser.git
cd openbrowser
docker compose up -dCDP is exposed on localhost:9222. Use the same MCP config as above.
# Verify
curl http://localhost:9222/json/versionThe chrome-profile volume persists across container restarts, so your logins survive.
Security note: CDP has no authentication. The compose file binds to 127.0.0.1 only, but other containers on the same Docker network can reach CDP directly. Do not place OpenBrowser on a shared network with untrusted containers.
Run the verification script to check your setup:
./scripts/verify.shIt checks: Chrome binary, profile directory, CDP connectivity, running services, and MCP configuration.
./scripts/uninstall.shStops services, removes service files and cron entries. Asks before deleting the Chrome profile. Does not uninstall Chrome itself.
The server install script sets these up automatically. For manual setup:
# Health check every 5 minutes (restarts Chrome if unresponsive)
(crontab -l 2>/dev/null; echo "*/5 * * * * /opt/openbrowser/scripts/session-check.sh") | crontab -
# Daily profile backup at 3am (keeps 7 days)
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/openbrowser/scripts/backup-profile.sh") | crontab -systemctl stop openbrowser
tar xzf /opt/openbrowser/backups/chrome-profile-YYYYMMDD.tar.gz -C ~/.config/
systemctl start openbrowserIf your accounts use TOTP-based 2FA (datacenter IPs often trigger re-verification):
echo "YOUR_TOTP_SECRET" > ~/.config/openbrowser/.totp-secret
chmod 600 ~/.config/openbrowser/.totp-secret
# Generate a code
/opt/openbrowser/scripts/generate-totp.shCDP not responding (curl: (7) Failed to connect)
- Local mode: check service is running (
launchctl list | grep openbrowseron macOS,systemctl --user status openbrowser-localon Linux) - Remote mode: check the SSH tunnel (
ps aux | grep autossh) and server service (systemctl status openbrowser) - Docker:
docker compose logs openbrowser - Check if something else is on port 9222:
lsof -i :9222
Session expired (Google/LinkedIn asks to log in again)
- Google sessions expire after ~30 days on trusted devices, sooner from datacenter IPs
- Remote mode: re-run
scripts/vnc-login.shand log in again via VNC - Local mode:
./scripts/login-local.sh - Set up TOTP automation (see 2FA Automation) to handle re-verification prompts
Chrome crashes or high memory
- Check shared memory: Chrome needs adequate
/dev/shm(Docker:shm_size: 2gb) - Check available RAM: Chrome with a few tabs uses 500MB-1GB
- Remote mode: check
journalctl -u openbrowser --no-pager -n 50
MCP not connecting
- Run
./scripts/verify.shto check all components - Verify your MCP config points to
http://localhost:9222(nothttps) - Restart your MCP client after config changes
All scripts use environment variables with sensible defaults. See env.example for the full list.
If you're automating interactions with services like LinkedIn, respect their rate limits. See rate-limits.example.json for recommended thresholds.
- No ports exposed to the internet. CDP only listens on
127.0.0.1. Access is exclusively through SSH (remote mode) or localhost (local/Docker mode). - VNC is ephemeral. The login script uses
-once, which auto-terminates after the first disconnect. - TOTP secrets are outside the Chrome profile and excluded from git via
.gitignore. - Profile backups stay on the server. They exclude cache directories to minimize size.
- Chrome updates are pinned (server mode) to prevent auto-updates from breaking authenticated sessions.
- Docker uses a non-root user and dedicated shared memory allocation.
- Server mode runs as root by default. Acceptable for single-purpose VPS setups. On shared servers, create a dedicated user and set
User=in the systemd service. See SECURITY.md for details.
openbrowser/
scripts/
chrome-launcher.sh # Chrome with Xvfb and CDP (remote/server mode)
chrome-launcher-local.sh # Chrome headless with CDP (local mode)
install-server.sh # One-command server setup
install-local.sh # One-command local setup
login-local.sh # Log into accounts in local mode
setup-tunnel.sh # Persistent SSH tunnel setup (remote mode)
verify.sh # Health verification
uninstall.sh # Clean removal
session-check.sh # Health check, restarts on failure
backup-profile.sh # Daily profile backup
vnc-login.sh # One-time VNC for manual login (remote)
generate-totp.sh # TOTP code generation for 2FA
systemd/
openbrowser.service # Chrome systemd service (server)
openbrowser-local.service # Chrome systemd service (local Linux, template)
cdp-tunnel.service # SSH tunnel systemd service (template)
launchd/
com.openbrowser.local.plist # Chrome launchd service (local macOS, template)
com.openbrowser.cdp-tunnel.plist # SSH tunnel launchd (macOS, template)
Dockerfile # Docker image
docker-compose.yml # Docker Compose config
env.example # Configuration reference
rate-limits.example.json # Rate limit guidelines
SECURITY.md # Security policy
The browser is the universal API. Every web service already works with it.
- Most apps don't expose everything via API (LinkedIn, Booking.com, airline sites)
- APIs require individual auth setup, rate limit management, pagination handling
- One Google login gives you OAuth SSO into dozens of services
- If you can see it on screen, the agent can interact with it
Anything that supports MCP or CDP:
- Claude Code (via Playwright MCP)
- Cursor (via MCP)
- Windsurf (via MCP)
- OpenCode (via MCP)
- Any Playwright or Puppeteer script (connect to
localhost:9222directly)
See CONTRIBUTING.md.
MIT