This template uses the coder-login module + Coder REST API to dynamically create additional workspaces from within the startup script.
- User creates a workspace with
additional_workspaces=Nparameter - The primary workspace starts normally with full K8s resources
- The startup script uses
coder-loginmodule to authenticate - Once authenticated, it uses the Coder CLI to create N additional workspaces
- Child workspaces are created with
additional_workspaces=0to prevent infinite loops
✅ Pure Terraform - No external scripts needed
✅ Single Template - Reuses the same template for children
✅ Automatic Loop Prevention - Detects child workspaces by naming pattern
✅ Authenticated - Uses coder-login module for secure API access
✅ Idempotent - Won't recreate existing workspaces
- additional_workspaces: Number of extra workspaces to create (0-9)
- cpu: CPU cores per workspace (2-8)
- memory: Memory in GB per workspace (2-8)
- home_disk_size: Persistent storage in GB (1-99999)
cd api-based-template
coder templates push kubernetes-multi \
--variable namespace=coder \
--variable use_kubeconfig=false# Create primary workspace that spawns 3 children
coder create my-team \
--template kubernetes-multi \
--parameter additional_workspaces=3 \
--parameter cpu=4 \
--parameter memory=8This creates:
my-team(primary workspace with full resources)my-team-1(child workspace)my-team-2(child workspace)my-team-3(child workspace)
coder list
# Shows: my-team, my-team-1, my-team-2, my-team-3
coder ssh my-team-1
coder open my-team-2 --app code-serverThe startup script contains this logic:
# 1. Authenticate using coder-login module
${module.coder_login.init_script}
# 2. Get current workspace name and template
WORKSPACE_NAME="${data.coder_workspace.me.name}"
TEMPLATE_NAME="${data.coder_workspace_build_info.me.template_name}"
# 3. Prevent infinite loop - skip if this is already a child
if echo "$WORKSPACE_NAME" | grep -qE '\-[0-9]+$'; then
echo "This is a child workspace, skipping"
else
# 4. Create N children using Coder CLI
for i in $(seq 1 "$ADDITIONAL_COUNT"); do
CHILD_NAME="$${WORKSPACE_NAME}-$${i}"
coder create "$CHILD_NAME" \
--template "$TEMPLATE_NAME" \
--parameter additional_workspaces=0 \ # Prevent recursion
--parameter cpu=${cpu} \
--parameter memory=${memory} \
--yes
done
fiThe template has TWO mechanisms to prevent infinite workspace creation:
- Naming Pattern Detection: If workspace name ends with
-<number>, it's a child and skips creation - Parameter Override: Child workspaces are created with
additional_workspaces=0
User Creates: my-team (additional_workspaces=3)
│
├─ Primary Workspace: my-team
│ ├─ Kubernetes Deployment
│ ├─ PVC
│ ├─ Agent with coder-login
│ └─ Startup Script:
│ ├─ Authenticates via coder-login
│ └─ Creates children via API
│
└─ Creates via API:
├─ my-team-1 (additional_workspaces=0)
├─ my-team-2 (additional_workspaces=0)
└─ my-team-3 (additional_workspaces=0)
| Feature | This Approach | CLI Scripts | coderd Provider |
|---|---|---|---|
| Terraform Native | ✅ Yes | ❌ No | ✅ Yes |
| Single Template | ✅ Yes | ✅ Yes | ❌ No (needs 2) |
| Works Today | ✅ Yes | ✅ Yes | ❌ No |
| Declarative | ❌ No | ✅ Yes | |
| Loop Prevention | ✅ Built-in | N/A | N/A |
| External Deps | ✅ None | ❌ Scripts | ✅ None |
- Sequential Creation: Children are created one-by-one in startup script (not parallel)
- Startup Time: Primary workspace takes longer to become "ready" while creating children
- Max 9 Children: Limited to 0-9 for simplicity (can be increased)
- Naming Convention: Must follow
name-Npattern for loop detection - Same Template: All children use the same template as parent
- Manual Cleanup: Deleting primary doesn't auto-delete children
Deleting the primary workspace does NOT delete children:
# Manual cleanup required
coder delete my-team-1 --yes
coder delete my-team-2 --yes
coder delete my-team-3 --yes
coder delete my-team --yesOr use the CLI script:
# From parent directory
./coder-multi-delete.sh my-team 3Check the primary workspace agent logs:
coder ssh my-team
cat /tmp/coder-agent.log | grep -A 20 "Creating additional"The coder-login module should handle this, but verify:
coder ssh my-team
which coder
coder versionThe coder-login module authentication might have failed:
coder ssh my-team
coder login --checkCheck workspace names - children should end with -<number>:
coder list
# Good: my-team, my-team-1, my-team-2
# Bad: my-team, my-team-child, my-team-child-1 (would loop)Modify the loop detection in coder.tf:
# Instead of checking for -N suffix, use a tag
if [ -f /home/coder/.is-child-workspace ]; then
echo "This is a child workspace, skipping"
else
# Create children and tag them
for i in $(seq 1 "$ADDITIONAL_COUNT"); do
coder create "$CHILD_NAME" ...
coder ssh "$CHILD_NAME" -- "touch /home/coder/.is-child-workspace"
done
fi# Modify the creation loop to pass different params
for i in $(seq 1 "$ADDITIONAL_COUNT"); do
CPU=$((2 * i)) # Double CPU for each child
MEMORY=$((4 * i)) # More memory
coder create "$CHILD_NAME" \
--parameter cpu=$CPU \
--parameter memory=$MEMORY \
...
doneInstead of Coder CLI, use curl with the API:
# Get template version ID
TEMPLATE_VERSION_ID=$(curl -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
"$CODER_URL/api/v2/templates/$TEMPLATE_ID/versions/latest" | jq -r .id)
# Create workspace via API
curl -X POST "$CODER_URL/api/v2/organizations/$ORG_ID/workspaces" \
-H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "my-team-1",
"template_id": "'$TEMPLATE_ID'",
"rich_parameter_values": [
{"name": "additional_workspaces", "value": "0"},
{"name": "cpu", "value": "4"}
]
}'Add metadata to track child creation status:
metadata {
display_name = "Child Creation Status"
key = "child_status"
script = <<EOT
if [ -f /tmp/child-creation-complete ]; then
echo "✅ Complete"
elif [ -f /tmp/child-creation-inprogress ]; then
echo "⏳ In Progress"
else
echo "⏸️ Not Started"
fi
EOT
interval = 30
timeout = 1
}main.tf- Kubernetes resources (deployment, PVC)coder.tf- Agent, parameters, apps, child creation logicproviders.tf- Provider configurationvariables.tf- Template variablesREADME.md- This file
- Review the startup script in
coder.tf - Customize loop detection if needed
- Test with small numbers first (
additional_workspaces=1) - Push template and create workspace
- Monitor logs during child creation