Skip to content
Documentation menu

Deployment — Docker & CDP endpoint

Run Clearcote as a standing CDP endpoint and point any existing framework at it — Playwright, Puppeteer, browser-use, Crawl4AI, Stagehand — with no code change. It launches the binary directly (no--enable-automation), so navigator.webdriver stays false: stealthy by construction.

Official Docker image

Pull the image and go. Any CDP client attaches over the exposed port.

bash
docker run -d --rm -p 9222:9222 teamflatearth/clearcote   # CDP on http://localhost:9222
python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp("http://localhost:9222")   # your code, unchanged
    page = browser.new_page()
    page.goto("https://example.com")
    print(page.title())

The image bakes in the SHA-256-verified Linux binary, a base font set and the Windows metric-clone fonts (so canvas/text hashes are coherent), and defaults to a coherent native Linux persona. Configure it entirely with env vars:

bash
docker run -d -p 9222:9222 \
  -e CC_PLATFORM=windows \      # windows | linux | macos | android
  -e CC_FINGERPRINT=user-7423 \ # seed -> stable identity
  -e CC_BRAND=Edge \            # Chrome (default) | Edge
  -e CC_ACCEPT_LANGUAGE=en-US \
  -e CC_TIMEZONE=America/New_York \
  -e CC_TLS_PROFILE=chrome-149 \
  teamflatearth/clearcote
Security: a CDP endpoint is full browser control. Publish it only to trusted networks —-p 127.0.0.1:9222:9222 keeps it host-local. The docker/ Dockerfile is auditable — rebuild and verify it yourself.

Standing CDP endpoint from the SDK — serve()

Launch the binary yourself and get a cdp_url any client attaches to. Same stealthy direct launch as the image, driven from your own process.

python
from clearcote import serve

srv = serve(fingerprint="seed-123", platform="windows")   # -> srv.cdp_url
# attach ANY CDP client:
#   playwright:  p.chromium.connect_over_cdp(srv.cdp_url)
#   puppeteer:   puppeteer.connect({ browserURL: srv.cdp_url })
#   browser-use / Crawl4AI / Stagehand: point them at srv.cdp_url
srv.close()
javascript
import { serve } from "clearcote";
const srv = await serve({ fingerprint: "seed-123", platform: "windows" });
// srv.cdpUrl -> connectOverCDP / puppeteer.connect({ browserURL })
await srv.close();

Or from the shell, without writing code:

bash
clearcote-serve --port 9222 --fingerprint seed-123 --platform windows   # prints http://127.0.0.1:9222

Direct — any CDP client, no SDK

Download the signed build from the Releases page, unzip, and drive it from stock Playwright with executable_path + --fingerprint switches:

python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(
        executable_path=r"C:\clearcote\chrome.exe",
        args=["--fingerprint=seed-123", "--fingerprint-platform=windows"],
    )
    browser.new_page().goto("https://example.com")

Build your own image (SDK-driven)

Clearcote ships a Linux x64 binary, so it runs headless in a container. The image needs the browser runtime libraries, a base font set (the #1 Linux tell if missing), and the SDK. On Linux the persona defaults to a coherent native Linux identity; WebRTC leak-proofing and Privacy-Sandbox-disable are on by default.

dockerfile
FROM node:20-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
      xz-utils libnss3 libnspr4 libgbm1 libasound2 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \
      libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 libxfixes3 libxext6 libxrender1 \
      libpango-1.0-0 libcairo2 libx11-6 libxcb1 libexpat1 libdbus-1-3 ca-certificates \
      fontconfig fonts-liberation fonts-noto-color-emoji fonts-unifont fonts-ipafont-gothic fonts-wqy-zenhei \
 && rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN npm i clearcote
RUN node --input-type=module -e "import { download } from 'clearcote'; await download();"   # bake the binary in
COPY run.mjs .
CMD ["node", "run.mjs"]
run.mjs
import { launchPersistentContext } from "clearcote";
const ctx = await launchPersistentContext("/tmp/prof", {
  headless: true,
  fingerprint: "user-1",
  proxy: { server: "http://gateway:8080", username: "u", password: "p" },
  geoip: true,      // timezone + languages + WebRTC IP matched to the proxy exit
  humanize: true,   // trusted bezier input; navigator.webdriver stays false
  args: ["--no-sandbox"],
});
const page = ctx.pages()[0] ?? (await ctx.newPage());
await page.goto("https://example.com");
await ctx.close();
Run the container with --shm-size=1g to avoid /dev/shm crashes on heavy pages. Python is identical (from clearcote import launch_persistent_context, snake_case options).

What attaches over CDP

ClientHow
Playwrightchromium.connect_over_cdp(url) / connectOverCDP(url)
Puppeteerpuppeteer.connect({ browserURL: url })
browser-use · Crawl4AI · Stagehandpoint their CDP/endpoint setting at the URL
Anything speaking CDPthe raw DevTools Protocol on the port

Want an AI agent to drive it instead? See the MCP server. Curious why a direct launch is stealthier? See How detection works.