VeilBrowserDocs

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:

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), 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):

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
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 -d

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:

  1. Create admin — paste the one-time setup token printed to the container logs (docker logs <container>), then set your admin username and password.
  2. 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.
  3. TLS — choose Cloudflare / HTTP (terminate TLS upstream) or upload your own certificate and key.
  4. Add worker — click Add local worker for a one-click worker on this host, or copy the docker run snippet to add a remote/BYO host.
  5. 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>/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.

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

terminal
sudo bash bootstrap.sh \
  --cluster-manager-tarball /path/to/cluster-manager.tar.gz \
  --release-tools-tarball   /path/to/release-tools.tar.gz

Start 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.

Next steps

Was this page helpful?

On this page