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.
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.
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.
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"]
}
}
}- The MCP server launches Chromium with
channel: "chrome", which looks for/opt/google/chrome/chrome - Our wrapper script at that path launches the real ARM64 Chromium binary with
--no-sandboxinjected - The
--no-sandboxflag is necessary because Docker containers don't support Chromium's sandbox (unprivileged user namespaces are restricted) - Xvfb provides a virtual display so the browser can render (even in headless mode, Chromium expects a display)
| 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 |
- 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-mcpand restart the Claude Code session.