Templates
Author or update Coder templates. A template is Terraform code that Coder runs to build a workspace; one template can back many workspaces.
Source of Truth
Read current upstream docs before applying anything topic-specific:
- https://coder.com/docs/llms.txt for the docs index.
- https://coder.com/docs/llms-full.txt only when the index is not enough.
- https://registry.coder.com/templates for the canonical starter templates and their import IDs.
This skill keeps only the authoring workflow, user interaction rules, and a few template-specific gotchas.
User Interaction
The user wants a working workspace, not a Terraform crash course.
- Default to a sensible starter for their infrastructure. Ask only to confirm.
- Show the planned tree (template name, files, parameters) before writing anything. Ask for one yes/no.
- Do not ask for cloud credentials in chat. Use the deployment's provisioner authentication; bring it up only when it is missing.
Workflow
1. Pick a starter
Map the user's intent to one of the official starters at https://registry.coder.com/templates:
docker,docker-devcontainer,docker-rstudio: container hosts.kubernetes,kubernetes-devcontainer,kubernetes-envbox: K8s clusters.aws-linux,aws-windows,aws-devcontainer: AWS EC2.gcp-linux,gcp-windows,gcp-vm-container,gcp-devcontainer: GCP Compute Engine.azure-linux,azure-windows: Azure VMs.digitalocean-linux: DigitalOcean Droplets.incus: LXD/Incus containers.nomad-docker: Nomad-driven Docker.scratch: empty template for advanced authors only.
Scaffold the chosen starter:
TEMPLATE_DIR="$(mktemp -d)/$TEMPLATE_NAME"
coder templates init --id "$STARTER_ID" "$TEMPLATE_DIR"
For an existing template the user wants to edit, pull instead:
TEMPLATE_DIR="$(mktemp -d)/$TEMPLATE_NAME"
coder templates pull "$TEMPLATE_NAME" "$TEMPLATE_DIR"
2. Understand the template anatomy
Every Coder template has these moving parts in main.tf:
terraformblock: required providers (alwayscoder/coder, plus the infrastructure provider).data "coder_workspace" "me"anddata "coder_workspace_owner" "me": workspace context, includingstart_count.data "coder_parameter"blocks: user inputs at workspace creation.- One
coder_agentresource: the agent that runs inside the workspace. coder_appresources: dashboard buttons for VS Code, JetBrains, Jupyter, and similar apps.- Infrastructure resources:
docker_container,kubernetes_deployment,aws_instance, etc., depending on the starter. - Optional
moduleblocks: Coder modules from registry.coder.com.
3. Modify
Apply the change the user asked for. Common patterns:
- Add
coder_parameters. Setmutable = truewhen the value should be changeable on rebuild. Usevalidationblocks for regex or range enforcement. Useoptionblocks for enums. - Add modules from https://registry.coder.com/modules. Defer module-specific syntax to the modules skill or to the module README.
- Persist data with a dedicated volume resource keyed to the workspace owner. Do not rely on container or VM filesystems to survive a rebuild.
- Set
count = data.coder_workspace.me.start_counton resources that should be torn down when the workspace stops.
Never store secrets in terraform.tfvars or pass them via plain
--variable. Use Coder's secret variables or external provisioners.
4. Validate locally
cd "$TEMPLATE_DIR"
terraform init
terraform fmt
terraform validate
5. Push
First push:
coder templates create "$TEMPLATE_NAME" -d "$TEMPLATE_DIR" --yes
Update an existing template:
coder templates push "$TEMPLATE_NAME" -d "$TEMPLATE_DIR" --yes
coder templates versions list "$TEMPLATE_NAME"
Promote the new version with --activate on push, or afterwards with
coder templates versions promote.
6. Test
Create one workspace from the new version:
coder create "$WORKSPACE_NAME" \
--template "$TEMPLATE_NAME" \
--yes
Pass every required parameter explicitly. For list parameters with no
sensible value, use []. For enums, use the first option. Ask the
user only when no default makes sense.
Wait until the agent reaches ready, not just until the build
finishes. If the agent stays in connecting, the workspace cannot
be used.
7. Hand off
If this was the user's first template, end with:
- Where the template lives in the dashboard.
- One workspace name they can run
coder sshinto. - One sentence about updates: edit
main.tf, thencoder templates push.
Common Parameters
Worth knowing because users ask for them often:
git_repo_url: URL to clone on workspace start. Pair with thegit-clonemodule.region: provider-specific region picker. Pair with the matching*-regionmodule.instance_typeornode_size: provider-specific machine size with anoptionlist.dotfiles_uri: clone a personal dotfiles repo on start. Pair with thedotfilesmodule.vscode_binary_version: optional pin forcode-server.
variable vs. coder_parameter
Use a Terraform variable (set via --variable at coder templates push time) for infrastructure-level choices that are fixed for
the entire template deployment and set by the admin:
- CPU architecture of the host machine (
arch) - Storage pool or volume group name
- Remote host identifier or cluster endpoint
Use data "coder_parameter" for choices the workspace user makes
at creation or build time:
- OS image or distribution
- CPU count, memory, and disk size
- Any value that legitimately differs between workspaces
Never expose infrastructure details as coder_parameter. Users
should not need to know the host's CPU architecture or the name of
a storage pool.
# Good: arch is an admin concern, not a user choice
variable "arch" {
description = "CPU architecture of the VM host (amd64 or arm64). Set at template push time."
type = string
default = "amd64"
}
# Good: image is a user choice made per workspace
data "coder_parameter" "image" {
name = "image"
type = "string"
default = "ubuntu/noble/cloud"
option {
name = "Ubuntu 24.04 LTS"
value = "ubuntu/noble/cloud"
}
}
Pass variable values at push time:
coder templates push my-template -d . --yes \
--variable arch=arm64 \
--variable storage_pool=fast-nvme
Deprecated coder_agent Fields
Do not set dir on coder_agent. It is deprecated in recent
provider versions, generates warnings on every coder templates push,
and breaks Coder Desktop file sync. The agent always starts in
$HOME by default.
# Wrong: deprecated, causes warnings
resource "coder_agent" "main" {
dir = "/home/${local.username}"
}
# Correct: omit dir entirely
resource "coder_agent" "main" {
arch = var.arch
os = "linux"
}
Contributing Templates to coder/registry
Before opening a PR, read the registry's AGENTS.md for code style, structure requirements, and the full PR review checklist. Also read the PR template for the required PR body format.
Additional points specific to templates (not covered in AGENTS.md):
Templates live at
registry/<namespace>/templates/<name>/withmain.tfandREADME.md. No.tftest.hclis required (only modules need tests).PR title convention:
feat(<namespace>/templates/<name>): <short description>(e.g.feat(bpmct/templates/incus-vm): add Incus VM template).Sync your fork's
mainwith upstream before branching. If you don't, the PR diff will show all pre-existing files in your namespace as new additions rather than just your changes:gh api -X POST /repos/<your-username>/registry/merge-upstream \ -f branch=mainRun
bun fmt(which runsterraform fmt+ Prettier) and confirm it produces no diff before pushing.
Safeguards
- Do not push a template version that has not passed
terraform validate. - Do not archive or delete an active template version without confirming nothing depends on it.
- Do not pass
--activateoncoder templates pushif the user is still iterating. They may want to review the new version first. - Do not place cloud credentials, OAuth secrets, or workspace tokens
in
main.tforterraform.tfvars. Use secret variables. - Do not recommend
scratchto a user who does not already author Terraform.
Bundled Resources
No per-template recipes ship with this skill. Defer to each template's README on registry.coder.com and to upstream Coder docs for provider-specific detail.