Skip to content

Instantly share code, notes, and snippets.

@statico
Created February 28, 2026 23:21
Show Gist options
  • Select an option

  • Save statico/56e3a1e3faed9ad4c6242897139424af to your computer and use it in GitHub Desktop.

Select an option

Save statico/56e3a1e3faed9ad4c6242897139424af to your computer and use it in GitHub Desktop.
Playwright MCP in Docker (ARM64)

Playwright MCP in Docker (ARM64)

Problem

The Playwright MCP server (@playwright/mcp) defaults to Chrome (channel: "chrome"), expecting it at /opt/google/chrome/chrome. Chrome has no ARM64 Linux binaries, so it fails immediately. The browser_install MCP tool also fails on ARM64.

Claude Code ignores args and env fields in ~/.claude/mcp.json for the playwright MCP server — it always launches npm exec @playwright/mcp@latest with no arguments and no custom env vars. So --browser firefox, env vars like PLAYWRIGHT_MCP_BROWSER, and custom command wrapper scripts all have no effect.

Fix

Trick the MCP server into using Playwright's ARM64 Chromium by placing a --no-sandbox wrapper script at the path it expects Chrome. This is the only approach that works given Claude Code's constraints.

Copy-paste setup script

Run this entire block to set up Playwright MCP in an ARM64 Docker container:

# 1. Install @playwright/test globally (needed for `npx playwright install` to work)
sudo npm install -g @playwright/test

# 2. Install Chromium browser (ARM64 binaries are available; Chrome's are not)
npx playwright install chromium

# 3. Install system dependencies (xvfb, fonts, GL libs)
npx playwright install-deps chromium

# 4. Install libraries that install-deps misses
sudo apt-get install -y libnss3 libnspr4

# 5. Find the installed Chromium binary path (version number changes with updates)
CHROMIUM_BIN=$(ls -d $HOME/.cache/ms-playwright/chromium-*/chrome-linux/chrome | head -1)

# 6. Create wrapper script at Chrome's expected path
# The wrapper injects --no-sandbox because containers can't use Chromium's sandbox
sudo mkdir -p /opt/google/chrome
printf "#!/bin/bash\nexec ${CHROMIUM_BIN} --no-sandbox \"\$@\"\n" | sudo tee /opt/google/chrome/chrome > /dev/null
sudo chmod +x /opt/google/chrome/chrome

# 7. Auto-start Xvfb (virtual display) on shell login
grep -q 'pgrep Xvfb' ~/.bashrc 2>/dev/null || echo 'pgrep Xvfb > /dev/null || Xvfb :99 -screen 0 1280x720x24 &' >> ~/.bashrc

# 8. Start Xvfb now
pgrep Xvfb > /dev/null || Xvfb :99 -screen 0 1280x720x24 &

echo "Done. Restart your Claude Code session for the MCP server to pick up the changes."

After running the script, restart the Claude Code session. The playwright MCP tools should work immediately.

mcp.json

The default ~/.claude/mcp.json config is fine — Claude Code ignores it anyway and always runs npm exec @playwright/mcp@latest:

{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": ["-y", "@playwright/mcp@latest"]
    }
  }
}

Why this works

  1. The MCP server launches Chromium with channel: "chrome", which looks for /opt/google/chrome/chrome
  2. Our wrapper script at that path launches the real ARM64 Chromium binary with --no-sandbox injected
  3. The --no-sandbox flag is necessary because Docker containers don't support Chromium's sandbox (unprivileged user namespaces are restricted)
  4. Xvfb provides a virtual display so the browser can render (even in headless mode, Chromium expects a display)

What DOESN'T work (and why)

Approach Why it fails
--browser firefox in mcp.json args Claude Code ignores mcp.json args entirely
PLAYWRIGHT_MCP_BROWSER=firefox in mcp.json env Claude Code ignores mcp.json env entirely
Custom command in mcp.json Claude Code ignores it, always runs npm exec @playwright/mcp@latest
browser_install MCP tool Always tries Chrome, which has no ARM64 binaries
sysctl to enable unprivileged user namespaces Restricted/blocked inside Docker containers
Symlink instead of wrapper script Can't inject --no-sandbox, so sandbox check fails

Maintenance

  • The Chromium binary path includes a version number (e.g., chromium-1208). After updating Playwright, re-run step 5-6 of the setup script to update the wrapper.
  • If the MCP server gets stuck on stale processes, kill them with pkill -f playwright-mcp and restart the Claude Code session.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment