VeilBrowserDocs

Installation

Install the cluster manager with Docker — one container, one command, one key to obtain.

Docker is the only supported way to install the control plane. It ships as a single hardened container with named volumes for state, the release webroot and the load-balancer config. You boot one container, and the only value you have to supply is your organization key (VEIL_KEY) — the installer derives or generates everything else, including the auth secret. A first-run wizard walks you through creating the admin, configuring the cluster and TLS, and adding your first worker — all from the UI.

Get your org API key

Sign in at veilbrowser.net and open Connect. Copy your organization API key — it authorizes both the private image pull and the browser runtime. The Connect page also shows the lowercased form used in the registry path.

One key, two roles

Your org key is the only secret you have to obtain. Its lowercased form is the registry path segment (registry.veilbrowser.net/<org-key-lower>/…) and the same key is passed to the runtime as VEIL_KEY. Treat it like a password and rotate it if it leaks. The compose file also wants an AGENT_TOKEN, but that is generated locally rather than issued to you.

Install with one command

On a fresh host, run this once. It installs Docker if it's missing, pulls the image through the registry gate (no docker login, no source code), writes a minimal .env, and starts the manager:

terminal
curl -fsSL https://veilbrowser.net/install.sh | VEIL_KEY=<your-org-key> bash

VEIL_KEY is the only input. The installer derives VEIL_ORG_KEY_LOWER (the lowercased key used as the registry path segment) and DOCKER_GID (so the wizard can provision same-host workers), generates an AGENT_TOKEN for later self-update, writes all four to .env in /opt/veil-cluster, and runs docker compose up -d. Pipe to bash without VEIL_KEY= to be prompted for the key.

Manual — what the script does. If you'd rather inspect and run the steps yourself (still no source, no docker login):

terminal
# 1. Docker + the Compose v2 plugin (skip if already installed)
curl -fsSL https://get.docker.com | sh

# 2. Fetch the compose file
mkdir -p /opt/veil-cluster && cd /opt/veil-cluster
curl -fsSL https://veilbrowser.net/deploy/compose.yaml -o compose.yaml

# 3. Write .env — VEIL_KEY is the only value you supply; the rest is derived
#    or generated here, exactly as install.sh would
printf 'VEIL_KEY=%s\nVEIL_ORG_KEY_LOWER=%s\nDOCKER_GID=%s\nAGENT_TOKEN=%s\n' \
  "<your-org-key>" "<your-org-key-lowercased>" \
  "$(getent group docker | cut -d: -f3)" "$(openssl rand -hex 32)" > .env

# 4. Bring it up
docker compose up -d

AGENT_TOKEN is not optional, even with self-update off

The compose file guards the updater sidecar with ${AGENT_TOKEN:?}, and Compose interpolates every service before it filters by profile — so an absent token fails up even though the updater profile is off. The guard is strict on purpose: an empty token makes the agent crash-loop. install.sh generates one for exactly this reason, which is why the command above does too.

The image is pulled through the registry gate at registry.veilbrowser.net/<org-key-lower>/cluster-manager — the gate authenticates the private upstream for you, so no docker login is needed.

On first boot the container auto-generates and persists its auth secret, applies database migrations in-process, and renders a placeholder config so the UI answers immediately. State lives in named volumes (cm-state, cm-webroot, cm-openresty), so upgrades and restarts keep your data.

Why mount the Docker socket?

The compose file mounts /var/run/docker.sock so the wizard can provision a worker on the same host with one click. Remove that mount (and group_add) and you can still add workers manually with a copy-paste docker run snippet.

Run the first-run wizard

Open the UI (https://<your-host>/). Because the database is empty, you land in the setup wizard, which walks through five steps:

  1. Create admin — paste the one-time setup token from the container logs.
  2. Cluster basics — name the cluster and set its hostnames.
  3. TLS — choose Cloudflare, your own certificate, or automatic HTTPS.
  4. Add worker — provision one on this host, or copy a docker run snippet.
  5. ReviewRender & Reload the load-balancer config and health-check the cluster.

Every field is explained in the onboarding walkthrough, and the TLS options in Edge security & TLS.

Connect your automation

The wizard finishes with your connect URL. Point Puppeteer or Playwright at the cluster's launch endpoint:

wss://<public-hostname>/launch

DNS records: one or two

The simplest deployment needs a single DNS record — the main hostname serves the console and the client wss://…/launch endpoint together, and same-box workers reach the release origin over the internal Docker network. Add a second record (release.<domain>) only when you run workers on other servers, so those remote workers can pull browser + profile bundles. Workers themselves are never DNS-resolved by clients — the manager proxies to them by IP, so adding capacity on more servers never means adding hostnames.

Updating later

You can update from the console instead of the command line by enabling the optional updater-agent profile — a container can't recreate itself, so a small sidecar does it. See Upgrades.

Deployment templates

For shapes the wizard does not cover, two templates are published next to the compose file above and fetched the same way — still no source, no docker login:

TemplateUse
compose/single-host.yamlCluster manager, one worker and an updater agent on one box
compose/env.exampleThe .env that goes beside single-host.yaml
k8s/worker-cluster.yamlHorizontally scaled workers behind a LoadBalancer service
terminal
curl -fsSL https://veilbrowser.net/deploy/compose/single-host.yaml -o single-host.yaml
curl -fsSL https://veilbrowser.net/deploy/compose/env.example -o .env

Each opens with a header comment giving the commands to run it, so it is self-contained once downloaded. The template index covers the requirements they share.

Fill in VEIL_ORG_KEY_LOWER — your org key, lowercased — before bringing either up. It is the path segment the images are pulled through, so compose stops with an error naming the variable if you leave it blank. There is still no registry username or password: the gate authorizes on the path alone.

Managed platforms are not supported

Fly.io, Render and DigitalOcean App Platform cannot run a worker today. A worker needs the Chromium sandbox — privileged or SYS_ADMIN plus at least 1 GB of /dev/shm — and none of the three exposes those controls. Run workers on hosts or a Kubernetes cluster you control.

Workers have host requirements the manager does not — the Chromium sandbox, a sized /dev/shm, a writable install volume. Run a worker states them once and is the page to follow when you add capacity by hand instead of through the wizard.

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 own contract set on /health and read the cloud's at boot. The check is advisory, not a gate: a mismatched major prints INCOMPATIBLE saas-api contract and the worker keeps running, and an unreachable cloud is logged as unverified. Treat that line as a startup alarm worth wiring to your log monitoring, because nothing downstream will stop a worker from serving against a cloud it cannot talk to correctly.

Next steps

Was this page helpful?

On this page