Template variables
Canopy computes a set of variables per worktree and exposes them two ways: as ${VAR} templates inside provisioned file values, and as $VAR environment variables for setup, migrate, teardown, custom commands and services.
Variable reference#
| Variable | Value | Available in |
|---|---|---|
WT_SLUG | The worktree's folder name, lowercased, every non-alphanumeric character turned into _ | templates + commands |
WT_INDEX | The worktree's stable port index (main checkout = 0) | templates + commands |
WT_DB_NAME | <repo slug>_<worktree slug>, the worktree's isolated database name | templates + commands |
WT_<ID>_PORT | A service's effective port, by service id, uppercased | templates + commands |
WT_<NAME>_PORT | The same port, by service name, uppercased with non-alphanumerics turned into _ | templates + commands |
WT_PATH | Absolute path to this worktree | commands |
WTM_WORKTREE | Same as WT_PATH | commands |
REPO_PATH | Absolute path to the repository's main checkout | commands |
WTM_REPO | Same as REPO_PATH | commands |
PORT | This service's own effective port | a service's own command |
WM_PORT_<ID> | Back-compat alias of WT_<ID>_PORT | templates + commands |
WM_WT_SLUG | Back-compat alias of WT_SLUG | templates + commands |
Port variable naming#
A service's port is exposed under two names, because ids and names serve different audiences:
service { id: "srv-19", name: "Server", basePort: 3000 }
→ $WT_SRV_19_PORT (by id)
→ $WT_SERVER_PORT (by name)
→ $WM_PORT_SRV_19 (back-compat alias of the id form)The name form exists so a hand-written template can say ${WT_SERVER_PORT} whatever the internal id is. Ids generated by the UI, like svc-19, never matched what anyone would actually type. The name form never overwrites an id-based variable, and if two services produce the same name slug, the first one keeps it.
Worked examples#
Isolate the database#
"keys": { "PG_DB": "${WT_DB_NAME}" }Repository tooljet, worktree folder fix-history-state, and you get PG_DB=tooljet_fix_history_state.
Give a service its own port#
In the service's command, not in the env:
npm start -- --port $PORT$PORT is that service's own effective port. Use it whenever the process takes a port flag.
Point one service at another#
"keys": {
"PORT": "${WT_SERVER_PORT}",
"VITE_API_URL": "http://localhost:${WT_SERVER_PORT}",
"TOOLJET_HOST": "http://localhost:${WT_FRONTEND_PORT}"
}Cross-service references belong in the provisioned file, since both services need to agree on the value and the file is the one thing they both read.
Use the worktree's paths in a command#
# a custom command
cp $REPO_PATH/.env.secrets $WT_PATH/.env.secrets
psql $WT_DB_NAME -c 'select count(*) from users'
echo "provisioning index $WT_INDEX for $WT_SLUG"How they are computed#
The index is assigned (or looked up) before provisioning runs, so ${WT_INDEX} and every port variable are already right when the first file is written, and it's persisted immediately.
The database name is derived from the repository id and the worktree folder name. Nothing queries Postgres to build it.
Ports apply the override first, then basePort + index × 10. A service with no basePort produces no port variable at all.
Common mistakes#
$PORT only means "this service" inside a service command. There's no "this service" in setup or a custom command, so use $WT_<SERVICE>_PORT there.
Templates use ${…} braces. Commands can use $VAR or ${VAR}, since your shell expands them.
A variable that doesn't exist is left as it is rather than replaced with an empty string, so a typo shows up in the resulting file instead of silently blanking a value.