Quickstart

From zero to a fused worker in about ten minutes. Get the master running, register the admin, take a quick UI tour, then add your first worker host. By the end Portainer is managing containers on the worker through the encrypted mesh.

1. Prerequisites

ContextBay runs everywhere Docker runs. For the master:

  • Docker Engine 20.10+ on Linux amd64/arm64, macOS, or Windows via WSL2.
  • ~512 MB free RAM and ~1 GB disk for the master plus the cb-* sub-container fleet.
  • Ports 7480 (HTTP/UI) and 7481 (gRPC) reachable from your worker LAN.

For each worker host:

  • Docker Engine + ability to run a container with --privileged=false and a docker.sock bind mount.
  • Outbound HTTP to the master's LAN IP on 7480 (used only for the one-time enroll), and outbound TCP to the Headscale public port for the steady-state mesh.
  • Port 9100 free for the worker's local /metrics endpoint (commonly conflicts with node-exporter — stop or relocate it).

2. Clone and deploy the master

The repo ships an opinionated make deploy target that builds the image, recreates the contextbay container with the right volume mounts, and waits for /api/health to come back green.

git clone https://github.com/contextbay/contextbay.git
cd contextbay
make deploy

On the very first run the master:

  • Creates the three CB volumes (contextbay-data, contextbay-portainer-data, cb-n8n_contextbay-n8n-data).
  • Joins the contextbay-internal Docker network and starts cb-portainer on its socket.
  • Deploys the cb-* sub-container fleet (cb-headscale, cb-prometheus, cb-alertmanager, cb-grafana, cb-loki, cb-tempo, cb-pyroscope, cb-n8n, cb-wazuh, cb-ollama) as Portainer stacks.
  • Auto-generates a JWT signing secret + n8n encryption key and persists them in the database — never overwritten on subsequent deploys.

Verify the master is healthy:

curl -s http://localhost:7480/api/health
# {"status":"ok","version":"...","uptime_seconds":...}

If the call fails, tail the logs and look for the bootstrap banner:

docker logs -f contextbay

3. Register the admin user

Open http://localhost:7480 in your browser. The first-run flow shows a setup screen — enter a username and a password (8+ characters). The form posts to POST /api/auth/setup, which is permanently disabled after the first admin is created.

Do not call /api/auth/setup directly during scripted verification. That endpoint is meant for the UI bootstrap flow — once consumed, it returns 409 forever and you cannot create more admins through it. Use the Users page instead (/admin/users) to add additional admins or operators.

You should now see the Dashboard with green health pills for the master, Portainer, and the cb-* fleet.

4. UI tour

Spend two minutes clicking through the main pages so the rest of this guide makes sense:

  • /dashboard — fleet health, alert digest, latest activity, embedded Grafana iframes for the most-used dashboards.
  • /hosts — pending and enrolled hosts. This is where you add a worker and grab the install snippet.
  • /containers — every container on every endpoint Portainer knows about, with stats, logs, and exec-into-shell.
  • /workflows — the embedded n8n with the 36 seeded workflows. CB delegates workflow authoring to n8n; this page is the n8n iframe.
  • /grafana, /planner, /brain — observability, project planner, knowledge vault. All auto-provisioned.

5. Add your first worker host

On the master UI, go to Hosts → Add Host. Pick a hostname (e.g. <worker-1>) and submit. The master:

  1. Mints a one-time enroll token (~24h TTL) and stores a pending host record.
  2. Renders the install snippet pre-filled with CONTEXTBAY_MASTER_URL, CONTEXTBAY_MASTER_ADDR, CONTEXTBAY_NODE_NAME, CONTEXTBAY_ENROLL_TOKEN and CONTEXTBAY_SHARED_SECRET.

Copy the snippet and paste it on the worker host. It looks like this:

docker run -d \
  --name contextbay-worker \
  --restart unless-stopped \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v contextbay-worker-data:/var/lib/contextbay \
  -e CONTEXTBAY_MASTER_URL=http://<MASTER_LAN_IP>:7480 \
  -e CONTEXTBAY_MASTER_ADDR=<MASTER_MESH_IP>:7481 \
  -e CONTEXTBAY_NODE_NAME=<worker-1> \
  -e CONTEXTBAY_ENROLL_TOKEN=<one-time-token-from-ui> \
  -e CONTEXTBAY_SHARED_SECRET=<shared-secret-from-ui> \
  ghcr.io/contextbay/contextbay-worker:latest

<MASTER_MESH_IP> is the master's address on the Headscale mesh, in the 100.64.0.0/10 CGNAT range — the master is always assigned 100.64.0.1, so the shape is the same on every install.

See Worker Onboarding for what every variable means and why two master addresses exist.

6. Watch enrollment happen

Within a few seconds the worker boots, reads the env, and:

  1. Calls POST /api/enroll on CONTEXTBAY_MASTER_URL — the only call that ever uses the LAN URL.
  2. The master mints a Headscale pre-auth key and consumes the enroll token atomically.
  3. The worker's embedded tsnet client redeems the key against the Headscale control plane, joins the mesh, and starts a gRPC stream to CONTEXTBAY_MASTER_ADDR.

On the worker you should see:

docker logs -f contextbay-worker
# ...
# msg="enroll: posting bootstrap" url=http://<MASTER_LAN_IP>:7480
# msg="enroll: redeemed authkey, joining mesh"
# msg="worker enrolled" node_name=<worker-1> mesh_addr=<MASTER_MESH_IP>:7481
# msg="grpc heartbeat ok"

On the master's Hosts page, the host flips from pending to monitoring. The host detail page shows recent enroll attempts (and any rejected attempts with their reason codes — see Troubleshooting).

7. Fuse the host

Enrollment gets you monitoring (heartbeats, metrics, Docker events). To do container CRUD on the worker — start, stop, restart, redeploy — you fuse the host. Click Fuse on the host card. The master:

  1. Pushes a Portainer Edge agent stack to the worker via the worker agent.
  2. Waits for the Edge agent to register a new endpoint with the master's Portainer.
  3. Records the endpoint id on the host record and flips state to fused.

From now on every container action on this host routes through Portainer (no fallback to direct gRPC container CRUD exists). The fuse watchdog gives up after a deadline — failures show up in the Hosts page with a reason code.

What's next

  • Architecture — the three-circle model, sub-container fleet, and database schema.
  • Worker Onboarding — what every install env var means, the bootstrap call, fuse handshake, and persistence model.
  • Operations — make targets, volume protocol, auth recovery, log locations.
  • Observability — Prometheus, Grafana, Loki, Tempo, Pyroscope, and the host-onboarding counters.
  • Configuration — every TOML option and CONTEXTBAY_* env override.