Installation
Install the cluster manager with Docker or on bare metal with the bootstrap script.
There are two supported ways to install the control plane. Both end with a running cluster manager fronted by a load balancer.
The Docker distribution ships the control plane as a single hardened container with
named volumes for state, the release webroot and the load-balancer config. You boot
one container with one required secret (VEIL_KEY); the auth secret is
auto-generated. 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 need. 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.
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:
curl -fsSL https://veilbrowser.net/install.sh | VEIL_KEY=<your-org-key> bashVEIL_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), writes them 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):
# 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
printf 'VEIL_KEY=%s\nVEIL_ORG_KEY_LOWER=%s\nDOCKER_GID=%s\n' \
"<your-org-key>" "<your-org-key-lowercased>" "$(getent group docker | cut -d: -f3)" > .env
# 4. Bring it up
docker compose up -dThe 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:
- Create admin — paste the one-time setup token printed to the container logs
(
docker logs <container>), then set your admin username and password. - Cluster basics — name the cluster and set the main hostname, which serves
both the console (UI + API) and client connections (
wss://<host>/launch) on one domain, plus an optional release hostname for browser/profile bundle downloads. Advanced mode lets you split these into separate subdomains. - TLS — choose Cloudflare / HTTP (terminate TLS upstream) or upload your own certificate and key.
- Add worker — click Add local worker for a one-click worker on this host, or
copy the
docker runsnippet to add a remote/BYO host. - Review — confirm your choices and let the wizard Render & Reload the load-balancer config and health-check the cluster automatically.
Connect your automation
The wizard finishes with your connect URL. Point Puppeteer or Playwright at the cluster's launch endpoint:
wss://<public-hostname>/launchDNS 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.
Auto-update
An opt-in Watchtower profile can poll for new signed images and update the container automatically. It only rolls forward — see Upgrades for pinning and rollback.
The bootstrap script is a one-shot, idempotent provisioner: it installs the runtime and
load balancer, lays down a versioned install with a current symlink, writes the
service unit, and runs database migrations.
Run bootstrap
sudo bash bootstrap.sh \
--cluster-manager-tarball /path/to/cluster-manager.tar.gz \
--release-tools-tarball /path/to/release-tools.tar.gzStart the service
No admin password or auth secret to set — the service auto-generates and persists its auth secret on first boot. Just start (or restart) the systemd unit.
First login
Open the UI and complete the first-run wizard (it creates the admin from the setup token in the service logs, then walks you through cluster basics, TLS and your first worker). Then run Render & Reload to write the real load-balancer config.
The setup wizard applies here too
On a fresh install with no admin, the same first-run wizard (create admin → cluster basics → TLS → add worker → Render & Reload) drives onboarding on bare metal as well. Paste the setup token from the service logs to create the first admin.
Invoke via bash
Shipped scripts are not marked executable on purpose. Always run them as
sudo bash <path>.
Roles
The control plane has three roles:
admin
Manage users, apply config reloads (touches the live load balancer), delete releases.
operator
Edit the cluster and fleet, run releases, preview renders, manage uploads.
viewer
Read-only dashboards.