Per-stand configuration. Each subdirectory here corresponds to one Terraform workspace (= one isolated stand), and its name must match the workspace name:
./up.sh stress1 # runs `terraform workspace select -or-create stress1`
# and reads ../../workspaces/stress1/- Several independent stands from the same code. The
serverandrunnermodules derive everything per-stand fromterraform.workspace(local.prefix): resource names, the DigitalOcean project, VPC ranges and the public hostname<name>-lb.<parent_domain>(e.g.stress1-lb.semaphoreui.dev). Spinning up a second stand is just running the scripts with a different name — no code changes, no state collisions. - Config lives outside the modules.
serverandrunnerare separate Terraform root modules with separate state files. This directory is the place both read the same per-stand settings from, instead of duplicating tfvars in each module. - Hand-off between modules. The
serverapply mints an admin API token and drops it here; therunnerapply later picks it up to register runners through the Semaphore API. The filesystem is the channel between the two states.
workspaces/
example/ # committed template — copy it to start a new stand
config.yml
<name>/ # one directory per stand, gitignored (may hold secrets)
config.yml # stand definition, written by hand (required)
admin.token # admin API token, generated by `server` apply
Everything except example/ is gitignored (see the repo root
.gitignore): config.yml contains the subscription key and
admin.token grants admin access to the stand.
Read by both stand2/do/server and stand2/do/runner:
runners: # one entry per runner droplet
delta:
name: delta
runner_executor_type: docker # or "k8s"
cluster_ip_range: 10.10.13.0/24 # VPC range for the server cluster
runners_ip_range: 10.10.12.0/24 # VPC range for the runners (must differ)
subscription_key: <key> # Semaphore Pro subscription key
cluster: 3 # number of Semaphore UI droplets behind the LB
ha_disabled: false # true = run without HA modeCommenting runner entries in or out and re-applying the runner module is the
intended way to scale runners up and down.
Created by the server apply: after cloud-init finishes on the first cluster
droplet, Terraform mints an admin API token over SSH and saves its stdout
here. The runner module reads it to call the Semaphore API when registering
runners. If it is lost or invalid, taint/replace
terraform_data.fetch_admin_token in the server module to re-mint it.
cp -r workspaces/example workspaces/<name>
# edit workspaces/<name>/config.yml: set subscription_key, adjust runners/ranges
cd stand2/do
./up.sh <name>Keep <name> short and DNS-safe (lowercase, digits, hyphens) — it becomes
part of resource names and the public hostname <name>-lb.<parent_domain>.