Get started
Docker
One Frankensurf for every machine on your network, as a Firecrawl-compatible API or an MCP server.
Run Frankensurf once, in a container, and point every machine at it. The image has Chromium and the free stealth browsers installed, and keeps its route memory and jobs on a volume.
docker run -d --name frankensurf -p 3002:3002 --shm-size=1g \ -e FRANKENSURF_API_KEY=change-me -v frankensurf:/data \ ghcr.io/yail259/frankensurfThat’s the Firecrawl-compatible API on port 3002. Anything that takes a Firecrawl URL uses it with the key as its API key:
from firecrawl import Firecrawlapp = Firecrawl(api_key="change-me", api_url="http://frankensurf.local:3002")page = app.scrape("https://example.com/pricing", formats=["markdown"])As an MCP server
Section titled “As an MCP server”docker run -d --name frankensurf-mcp -p 8000:8000 --shm-size=1g \ -e FRANKENSURF_API_KEY=change-me -v frankensurf:/data \ ghcr.io/yail259/frankensurf frankensurf-mcp --http --host 0.0.0.0Then add it to a client as a streamable HTTP server at http://frankensurf.local:8000/mcp with the
header Authorization: Bearer change-me. In Claude Code:
claude mcp add --transport http frankensurf http://frankensurf.local:8000/mcp \ --header "Authorization: Bearer change-me"Compose
Section titled “Compose”services: frankensurf: image: ghcr.io/yail259/frankensurf restart: unless-stopped shm_size: 1gb ports: ["3002:3002"] environment: FRANKENSURF_API_KEY: change-me # Paid tools stay off unless you add their keys and start with --paid: # FRANKENSURF_FIRECRAWL_API_KEY: ... volumes: ["frankensurf:/data"]volumes: frankensurf:- Images:
ghcr.io/yail259/frankensurf:<version>and:latest, forlinux/amd64andlinux/arm64. Where a stealth browser isn’t built for a platform, that image still reads with plain HTTP, Jina Reader and Chromium. - Size: about 6 GB unpacked. It carries four browsers (Chromium twice, for Frankensurf and the stealth tools’ own Playwright, plus Camoufox’s Firefox), which is what gets it past walls.
--shm-size=1g: browsers need shared memory; Docker’s default 64 MB makes them crash.- Chrome’s sandbox: Docker blocks the user namespaces it needs, so inside the container the stealth Chromium runs without it, the same as Playwright’s own Chromium does by default; the container is the isolation. Outside a container it stays on wherever the kernel allows it.
- Memory: about 1 GB for each browser read running at once.
- Agent mode: add
-e FRANKENSURF_AGENT_MODEL=...and the provider’s key to get the summary, json and extract endpoints (Agent mode). - The container runs as an unprivileged user, with
tinireaping browser processes.