Cluster mode
Launch browsers on demand over a single WebSocket endpoint, load-balanced across your worker fleet.
Cluster mode turns a local-api worker into an on-demand browser server: your
automation connects to wss://<host>/launch, a browser is spawned and licensed
per launch, and CDP is piped straight back. No REST pre-step, works with stock
Puppeteer / Playwright / any CDP client.
Enable it by running the worker with VEIL_SERVER_MODE=cluster.
Connect
import puppeteer from "puppeteer-core";
const env = {
VEIL_KEY: "<organization-api-key>",
// Launch an existing cloud profile…
profileId: "<profile-id>",
// …or create one from criteria:
// spec: { os: "win", browserVersion: 130 },
};
const url =
"wss://connect.example.com/launch?env=" +
encodeURIComponent(JSON.stringify(env));
const browser = await puppeteer.connect({ browserWSEndpoint: url });
const page = await browser.newPage();
await page.goto("https://example.com");import { chromium } from "playwright-core";
const env = { VEIL_KEY: "<organization-api-key>", profileId: "<profile-id>" };
const url =
"wss://connect.example.com/launch?env=" +
encodeURIComponent(JSON.stringify(env));
const browser = await chromium.connectOverCDP(url);
const page = await browser.contexts()[0].newPage();
await page.goto("https://example.com");# List live browsers on a worker
curl "http://localhost:38923/browsers?simple=true"Reconnect, VNC and devtools
Give a launch a stable referenceId to reconnect later, and address a specific
browser for VNC / devtools:
wss://<host>/launch/<referenceId>?reconnect=true— reattach or launch.GET /browsers/<id>/urls— worker-encoded URLs for CDP, devtools and VNC.GET /browsers/<id>/json— the browser's CDP page list.
Behind the cluster-manager load balancer these URLs path-encode the worker id
(/w/<workerId>/browsers/<id>/...) so follow-up requests always reach the
worker holding that browser.
Load balancing
Launching is stateless; everything after is sticky to one worker. The cluster-manager OpenResty LB handles both:
- Launch —
wss://connect.example.com/launchis spread across workers withleast_conn(approximating capacity-aware selection). A worker at capacity returns503before the upgrade, and the LB retries the next one. - Sticky ops —
/w/<workerId>/...routes deterministically to a specific worker. Single domain, single certificate, no wildcard DNS.
Enable it by setting clusterProxyEnabled on the Cluster row and running
Render & Reload. Set VEIL_WORKER_ID on each worker to match its FleetHost
hostname.
Capacity & lifecycle
Bound per-worker concurrency with VEIL_MAX_BROWSERS. Reap idle/abandoned
browsers with VEIL_AUTO_CLOSE=true (close on last client disconnect) and
VEIL_IDLE_BROWSER_TIMEOUT=<seconds>.
Licensing is fail-closed
Every launch calls POST /v1/license/authorize on the cloud. If the cloud is
unreachable or the org is at its concurrency limit, the launch is refused —
already-running sessions are never killed. Budget for one cloud round-trip per
launch.
Private images
The worker and cluster-manager images are pulled from the license-gated registry using your organization's API key, lowercased, as the path segment. The registry gate verifies it against the cloud before proxying to the upstream image:
docker pull registry.veilbrowser.net/<org-key-lower>/local-api:latest
docker pull registry.veilbrowser.net/<org-key-lower>/cluster-manager:latest<org-key-lower> is your VEIL_KEY in lowercase (the install.sh script sets
it for you as VEIL_ORG_KEY_LOWER). Registry gating is a convenience gate —
runtime authorize is the real license enforcement.
Versioning & channels
Each image is versioned independently from its own source. Pick a channel by tag:
:X.Y
Auto-patch within a minor. Recommended for production.
:X.Y.Z
Pinned/reproducible. Use for strict change control.
:canary
Built from dev — pre-merge testing.
:beta
Prerelease line for opt-in testers.
The /v1 cloud API is a frozen contract to self-hosted workers: additive-only
within v1; breaking changes ship as /v2 with an overlap window (workers
supported within 2 minors / 90 days). Workers advertise their contract on
/health and refuse to run against an incompatible cloud.
Deploy
Templates for single-host compose, Kubernetes and one-click clouds live in
deploy/. See
Upgrades for drain-then-roll worker updates.