Skip to content

Get started

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.

Terminal window
docker run -d --name frankensurf -p 3002:3002 --shm-size=1g \
-e FRANKENSURF_API_KEY=change-me -v frankensurf:/data \
ghcr.io/yail259/frankensurf

That’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 Firecrawl
app = Firecrawl(api_key="change-me", api_url="http://frankensurf.local:3002")
page = app.scrape("https://example.com/pricing", formats=["markdown"])
Terminal window
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.0

Then 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:

Terminal window
claude mcp add --transport http frankensurf http://frankensurf.local:8000/mcp \
--header "Authorization: Bearer change-me"
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, for linux/amd64 and linux/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 tini reaping browser processes.