Files
coder/docs/tutorials/template-from-scratch.md
T
Jeremy Ruppel 79fc8541ed docs: update template creation docs for template builder (#26993)
## Summary

Update documentation across 9 files to present the template builder as
the primary template creation method, replacing the old starter
templates flow as the default entry point.

The template builder is a guided wizard that lets admins select base
infrastructure, add registry modules, configure variables, and produce
validated Terraform without writing HCL.

## Changes

**Primary docs (significant rewrites):**
- `docs/admin/templates/creating-templates.md`: Added "Using the
template builder" as the first section with full 5-step wizard
documentation, screenshots, airgap/registry notes, and alternative
creation links. Moved CLI starter template flow to its own section.
Fixed "You can the" typo.
- `docs/get-started/index.md`: Rewrote Steps 4-6 to use the builder with
the Docker base template instead of the Coder Quickstart (which is not a
builder base template). Generalized workspace parameter instructions.
- `docs/start/first-template.md`: Rewrote to use the builder. Removed
old starter templates references, TODO notes, typo, and commented-out
sections.

**Secondary docs (targeted edits):**
- `docs/admin/templates/index.md`: Replaced starter templates section
with builder-first "Create a template" section.
- `docs/admin/templates/managing-templates/index.md`: Renamed "Starter
templates" to "Creating templates" pointing to the builder.
- `docs/install/airgap.md`: Added "Template builder" section documenting
`CODER_DISABLE_TEMPLATE_BUILDER` and
`CODER_TEMPLATE_BUILDER_REGISTRY_URL`.
- `docs/tutorials/template-from-scratch.md`: Added TIP callout
recommending the builder. Fixed `coder templates create` -> `coder
templates push` inconsistency.
- `docs/admin/integrations/devcontainers/envbuilder/add-envbuilder.md`:
Updated Dashboard tab to reference the builder and "Upload an existing
template" alternative.
- `docs/about/screenshots.md`: Updated caption and image reference for
template builder.

**Screenshots added:**
- `templatebuilder_01_bases.png` (base selection step)
- `templatebuilder_02_modules.png` (module selection step)
- `templatebuilder_03_module_customization.png` (module settings step)
- `templatebuilder_04_customizations.png` (template customizations step)

<details>
<summary>Implementation plan</summary>

# Plan: Update docs/ for Template Builder Launch

## Summary

The Template Builder is a new guided wizard at `/templates/new/builder`
that lets admins create templates by selecting a base infrastructure
template, composing it with registry modules, configuring variables, and
producing a validated Terraform bundle without writing HCL. The docs
need to be updated to present this as the primary/recommended template
creation path, while preserving the existing paths (upload, CLI,
duplicate) as alternatives.

## Key behavioral facts from the code

- **Route**: `/templates/new/builder` (new), `/templates/new` (old,
still exists)
- **Entry point**: The "New Template" button on the Templates page links
to `/templates/new/builder` when the builder is enabled; otherwise falls
back to `/starter-templates`
- **5-step wizard**:
  1. **Select base infrastructure** (e.g., Docker, AWS EC2, Kubernetes)
  2. **Base template parameters** (optional, skipped if base has none)
3. **Select modules** (IDE, AI Agent, Source Control, etc.;
multi-select, grouped by category)
4. **Module settings** (optional, skipped if no configurable variables)
5. **Template customizations** (name, display name, description, icon,
organization)
- **Alternative creation links** are shown on step 1: "Start from
scratch", "Upload an existing template", "Browse community templates",
"Use template agent skill"
- **Disabled via**: `CODER_DISABLE_TEMPLATE_BUILDER` env var /
`--disable-template-builder` flag. When disabled, redirects to old
`/templates/new` flow
- **Registry URL override**: `CODER_TEMPLATE_BUILDER_REGISTRY_URL`
(default: `registry.coder.com`)
- **Requires outbound access** to `registry.coder.com` for `terraform
init` at compose time
- **Modules are bundled** with the Coder release binary; the builder
does not fetch metadata from the registry at runtime
- **Sensitive variables** (secrets) are not collected by the builder;
they are deferred to workspace creation time
- **Module conflicts** show a warning but do not block creation
- **One-way**: No re-entry into the builder for existing templates; edit
HCL directly after creation

## Files to update

### Tier 1: Primary creation flow docs (significant rewrites)

#### 1. `docs/admin/templates/creating-templates.md`

**Current state**: Documents three creation paths: "From a starter
template" (primary), "From an existing template", "From scratch
(advanced)".

**Changes**:
- Add a new section **"Using the template builder"** as the first and
primary section (before "From a starter template").
- Describe the 5-step wizard flow: select base infrastructure, configure
base parameters, select modules, configure module settings, set template
customizations.
- Mention that the builder is enabled by default and requires outbound
access to `registry.coder.com`.
- Note that sensitive variables are collected from developers at
workspace creation, not during template building.
- Add a callout about disabling the builder for airgapped deployments
(`CODER_DISABLE_TEMPLATE_BUILDER`).
- Note the `CODER_TEMPLATE_BUILDER_REGISTRY_URL` option for self-hosted
registry mirrors.
- Keep existing "From a starter template", "From an existing template",
and "From scratch" sections largely intact, but reframe them as
alternative paths.
- Update the "From a starter template" Web UI instructions to note the
new entry point routing (the "New Template" button now goes to the
builder when enabled).
- Fix existing typo: "You can the [Coder CLI]" should be "You can use
the [Coder CLI]".

#### 2. `docs/start/first-template.md`

**Current state**: Beginner tutorial walking through creating a template
from the Docker starter template via the old flow. Has a typo (`s` at
end of line 32), commented-out workspace creation section, and TODO
notes.

**Changes**:
- Rewrite steps 2 and 3 to use the Template Builder as the primary path.
- Step 2: Navigate to **Templates**, select **New Template**, which
opens the Template Builder.
- Step 3: Walk through the builder wizard steps (select Docker base,
optionally select modules like code-server, configure template
name/description, create).
- Remove the typo on line 32 (`s`).
- Keep the "Modify your template" section (step 6) intact since it
covers post-creation editing which is unchanged.
- Remove or update the reference to "Starter Templates" as a separate
page since the builder subsumes that entry point.

#### 3. `docs/get-started/index.md`

**Current state**: Quickstart guide. Step 4 says "Select **Templates** →
**New Template**" then pick "Coder Quickstart" from starter templates.

**Changes**:
- Update Step 4 to describe using the Template Builder.
- The flow becomes: Select **Templates** → **New Template** → builder
opens → select **Coder Quickstart** as the base template → optionally
add modules → set name/description → **Create Template**.
- Update the "What just happened?" explanation to mention the builder
composed and validated the Terraform.
- Screenshot reference `create-quickstart-template.png` will need a new
screenshot (note this in the PR; screenshots are out of scope for this
change but should be flagged).

### Tier 2: Secondary references (targeted edits)

#### 4. `docs/admin/templates/index.md`

**Current state**: Overview page mentioning starter templates as the
primary creation path.

**Changes**:
- Update the "Starter templates" section to mention the Template Builder
as the recommended way to create templates, with starter templates
serving as base templates within the builder.
- Update the link to point to the builder section: `[Create a template
with the template
builder](./creating-templates.md#using-the-template-builder)`.
- Update the screenshot reference and caption. The "Starter Templates"
page screenshot may no longer be the first thing admins see.

#### 5. `docs/admin/templates/managing-templates/index.md`

**Current state**: Documents starter templates, editing, updating,
deleting.

**Changes**:
- Update the "Starter templates" section to mention the Template Builder
as the primary creation path, with starter templates available as base
templates within it.
- Update the image reference from `starter-templates.png` if it shows
the old flow.

#### 6. `docs/tutorials/template-from-scratch.md`

**Current state**: Detailed tutorial for writing a template from scratch
with Terraform.

**Changes**:
- Add a brief note at the top recommending the Template Builder for
users who want to create templates without writing Terraform, with a
link to
`docs/admin/templates/creating-templates.md#using-the-template-builder`.
- In section "7. Create the template in Coder" → "Dashboard" tab, update
the UI steps. The "Upload template" option is now accessed via the old
creation flow at `/templates/new` (or through the "Upload an existing
template" link in the builder's alternatives).
- Fix the inconsistency where text says `coder templates create` but the
code block uses `coder templates push`.

#### 7.
`docs/admin/integrations/devcontainers/envbuilder/add-envbuilder.md`

**Current state**: Documents creating envbuilder templates via
Dashboard, CLI, and Registry tabs.

**Changes**:
- In the Dashboard tab, update the instructions. The "Create Template"
button now opens the builder by default. Users need to use the "Upload
an existing template" alternative link or navigate to `/templates/new`
directly.
- Update "From scratch" reference since that option is now an
alternative link in the builder.
- The CLI and Registry tabs remain unchanged.

#### 8. `docs/install/airgap.md`

**Current state**: Documents air-gapped installations. No mention of
Template Builder.

**Changes**:
- Add a note in the relevant section about the Template Builder
requiring outbound access to `registry.coder.com`.
- Document `CODER_DISABLE_TEMPLATE_BUILDER` for fully air-gapped
deployments.
- Document `CODER_TEMPLATE_BUILDER_REGISTRY_URL` for deployments using a
self-hosted registry mirror.

#### 9. `docs/about/screenshots.md`

**Current state**: Contains a caption "Template administrators can
either create a new Template from scratch or choose a Starter Template".

**Changes**:
- Update the caption to mention the Template Builder as the primary
creation method.
- Screenshot reference may need updating (flag for new screenshot).

### Tier 3: Minor/link-only updates

#### 10. `docs/admin/users/organizations.md`

- If it references the old "Create Template" screen with an org picker,
add a note that the Template Builder also includes organization
selection in its final step.

#### 11. `docs/ai-coder/tasks.md`

- If it mentions creating templates, add a passing reference to the
Template Builder as an option.

## Files NOT to update

- `docs/reference/api/templatebuilder.md`: Auto-generated API reference.
Already correct.
- `docs/reference/api/schemas.md`: Auto-generated. Already correct.
- `docs/reference/cli/server.md`: Auto-generated. Already has
`--disable-template-builder` and `--template-builder-registry-url`.
- `docs/reference/cli/templates_create.md`: Already deprecated.
- `docs/reference/cli/templates.md`: No changes needed.

## Implementation order

1. `docs/admin/templates/creating-templates.md` (primary creation docs,
most content)
2. `docs/get-started/index.md` (quickstart)
3. `docs/start/first-template.md` (beginner tutorial)
4. `docs/admin/templates/index.md` (overview)
5. `docs/admin/templates/managing-templates/index.md` (managing
overview)
6. `docs/install/airgap.md` (airgap note)
7. `docs/tutorials/template-from-scratch.md` (from-scratch tutorial)
8. `docs/admin/integrations/devcontainers/envbuilder/add-envbuilder.md`
(envbuilder)
9. `docs/about/screenshots.md` (screenshot captions)
10. Minor link/reference updates in tier 3 files

## Style notes

- Follow the Diataxis framework; keep tutorials as tutorials, reference
as reference.
- Use present tense, active voice, second person.
- Bold for UI elements: **Templates**, **New Template**, **Create
Template**.
- No emdash/endash.
- Do not add screenshots; flag where new screenshots are needed as
comments/TODOs.
- Run `make fmt/markdown` and `make lint/markdown` after all changes.
- Verify all pages are already in `docs/manifest.json` (no new pages
being added, only existing pages being updated).

</details>

> 🤖 Generated by Coder Agents
2026-07-06 11:15:59 -04:00

14 KiB

Write a template from scratch

Tip

If you want to create a template without writing Terraform, use the template builder instead. The builder guides you through selecting base infrastructure and adding modules through a visual interface.

A template is a common configuration that you use to deploy workspaces.

This tutorial teaches you how to create a template that provisions a workspace as a Docker container with Ubuntu.

Before you start

You'll need a computer or cloud computing instance with both Docker and Coder installed on it.

What's in a template

The main part of a Coder template is a Terraform tf file. A Coder template often has other files to configure the other resources that the template needs. In this tour you'll also create a Dockerfile.

Coder can provision all Terraform modules, resources, and properties. The Coder server essentially runs a terraform apply every time a workspace is created, started, or stopped.

Tip

Haven't written Terraform before? Check out Hashicorp's Getting Started Guides.

Here's a simplified diagram that shows the main parts of the template we'll create:

Template architecture

1. Create template files

On your local computer, create a directory for your template and create the Dockerfile. You will upload the files to your Coder instance later.

mkdir -p template-tour/build && cd $_

Enter content into a Dockerfile that starts with the official Ubuntu image. In your editor, enter and save the following text in Dockerfile then exit the editor:

FROM ubuntu

RUN apt-get update \
    && apt-get install -y \
    sudo \
    curl \
    && rm -rf /var/lib/apt/lists/*

ARG USER=coder
RUN useradd --groups sudo --no-create-home --shell /bin/bash ${USER} \
    && echo "${USER} ALL=(ALL) NOPASSWD:ALL" >/etc/sudoers.d/${USER} \
    && chmod 0440 /etc/sudoers.d/${USER}
USER ${USER}
WORKDIR /home/${USER}

Dockerfile adds a few things to the parent ubuntu image, which your template needs later:

  • It installs the sudo and curl packages.
  • It adds a coder user, including a home directory.

2. Set up template providers

Edit the Terraform main.tf file to provision the workspace's resources.

Start by setting up the providers. At a minimum, we need the coder provider. For this template, we also need the docker provider:

terraform {
  required_providers {
    coder = {
      source  = "coder/coder"
    }
    docker = {
      source  = "kreuzwerker/docker"
    }
  }
}

locals {
  username = data.coder_workspace_owner.me.name
}

data "coder_provisioner" "me" {
}

provider "docker" {
}

provider "coder" {
}

data "coder_workspace" "me" {
}

data "coder_workspace_owner" "me" {
}

Notice that the provider blocks for coder and docker are empty. In a more practical template, you would add arguments to these blocks to configure the providers, if needed.

The coder_workspace data source provides details about the state of a workspace, such as its name, owner, and so on. The data source also lets us know when a workspace is being started or stopped. We'll use this information in later steps to:

  • Set some environment variables based on the workspace owner.
  • Manage ephemeral and persistent storage.

3. coder_agent

All templates need to create and run a Coder agent. This lets developers connect to their workspaces. The coder_agent resource runs inside the compute aspect of your workspace, typically a VM or container. In our case, it will run in Docker.

You do not need to have any open ports on the compute aspect, but the agent needs curl access to the Coder server.

Add this snippet after the last closing } in main.tf to create the agent:

resource "coder_agent" "main" {
  arch                   = data.coder_provisioner.me.arch
  os                     = "linux"
  startup_script         = <<-EOT
    set -e

    # install and start code-server
    curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server
    /tmp/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &
  EOT

  env = {
    GIT_AUTHOR_NAME     = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
    GIT_AUTHOR_EMAIL    = "${data.coder_workspace_owner.me.email}"
    GIT_COMMITTER_NAME  = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
    GIT_COMMITTER_EMAIL = "${data.coder_workspace_owner.me.email}"
  }

  metadata {
    display_name = "CPU Usage"
    key          = "0_cpu_usage"
    script       = "coder stat cpu"
    interval     = 10
    timeout      = 1
  }

  metadata {
    display_name = "RAM Usage"
    key          = "1_ram_usage"
    script       = "coder stat mem"
    interval     = 10
    timeout      = 1
  }
}

Because Docker is running locally in the Coder server, there is no need to authenticate coder_agent. But if your coder_agent is running on a remote host, your template will need authentication credentials.

This template's agent also runs a startup script, sets environment variables, and provides metadata.

  • startup script

    • Installs code-server, a browser-based VS Code app that runs in the workspace.

      We'll give users access to code-server through coder_app later.

  • env block

    • Sets environments variables for the workspace.

      We use the data source from coder_workspace to set the environment variables based on the workspace's owner. This way, the owner can make git commits immediately without any manual configuration.

  • metadata blocks

    • Your template can use metadata to show information to the workspace owner Coder displays this metadata in the Coder dashboard.

      Our template has metadata blocks for CPU and RAM usage.

4. coder_app

A coder_app resource lets a developer use an app from the workspace's Coder dashboard.

Apps in a Coder workspace

This is commonly used for web IDEs such as code-server, RStudio, and JupyterLab.

We installed code-server in the startup_script argument. To add code-server to the workspace, make it available in the workspace with a coder_app resource. See web IDEs for more examples:

resource "coder_app" "code-server" {
  agent_id     = coder_agent.main.id
  slug         = "code-server"
  display_name = "code-server"
  url          = "http://localhost:13337/?folder=/home/${local.username}"
  icon         = "/icon/code.svg"
  subdomain    = false
  share        = "owner"

  healthcheck {
    url       = "http://localhost:13337/healthz"
    interval  = 5
    threshold = 6
  }
}

You can also use a coder_app resource to link to external apps, such as links to wikis or cloud consoles:

resource "coder_app" "coder-server-doc" {
  agent_id     = coder_agent.main.id
  icon         = "/emojis/1f4dd.png"
  slug         = "getting-started"
  url          = "https://coder.com/docs/code-server"
  external     = true
}

5. Persistent and ephemeral resources

Managing the lifecycle of template resources is important. We want to make sure that workspaces use computing, storage, and other services efficiently.

We want our workspace's home directory to persist after the workspace is stopped so that a developer can continue their work when they start the workspace again.

We do this in 2 parts:

  • Our docker_volume resource uses the lifecycle block with the ignore_changes = all argument to prevent accidental deletions.
  • To prevent Terraform from destroying persistent Docker volumes in case of a workspace name change, we use an immutable parameter, like data.coder_workspace.me.id.

Later, we use the Terraform count meta-argument to make sure that our Docker container is ephemeral.

resource "docker_volume" "home_volume" {
  name = "coder-${data.coder_workspace.me.id}-home"
  # Protect the volume from being deleted due to changes in attributes.
  lifecycle {
    ignore_changes = all
  }
}

For details, see Resource persistence.

6. Set up the Docker container

To set up our Docker container, our template has a docker_image resource that uses build/Dockerfile, which we created earlier:

resource "docker_image" "main" {
  name = "coder-${data.coder_workspace.me.id}"
  build {
    context = "./build"
    build_args = {
      USER = local.username
    }
  }
  triggers = {
    dir_sha1 = sha1(join("", [for f in fileset(path.module, "build/*") : filesha1(f)]))
  }
}

Our docker_container resource uses coder_workspace start_count to start and stop the Docker container:

resource "docker_container" "workspace" {
  count = data.coder_workspace.me.start_count
  image = docker_image.main.name
  # Uses lower() to avoid Docker restriction on container names.
  name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
  # Hostname makes the shell more user friendly: coder@my-workspace:~$
  hostname = data.coder_workspace.me.name
  # Use the docker gateway if the access URL is 127.0.0.1
  entrypoint = ["sh", "-c", replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")]
  env = [
    "CODER_AGENT_TOKEN=${coder_agent.main.token}",
  ]
  host {
    host = "host.docker.internal"
    ip   = "host-gateway"
  }
  volumes {
    container_path = "/home/${local.username}"
    volume_name    = docker_volume.home_volume.name
    read_only      = false
  }
}

7. Create the template in Coder

Save main.tf and exit the editor.

Now that we've created the files for our template, we can add them to our Coder deployment.

We can do this with the Coder CLI or the Coder dashboard. In this example, we'll use the Coder CLI.

  1. Log in to your Coder deployment from the CLI. This is where you need the URL for your deployment:

    $ coder login https://coder.example.com
    Attempting to authenticate with config URL: 'https://coder.example.com'
    Open the following in your browser:
    
        https://coder.example.com/cli-auth
    
    > Paste your token here:
    
  2. In your web browser, enter your credentials:

    Log in to your Coder deployment

  3. Copy the session token to the clipboard:

    Copy session token

  4. Paste it into the CLI:

    > Welcome to Coder, marc! You're authenticated.
    $
    

Add the template files to Coder

Add your template files to your Coder deployment. You can upload the template through the CLI, or through the Coder dashboard:

CLI

  1. Run coder templates push from the directory with your template files:

    $ pwd
    /home/docs/template-tour
    $ coder templates push
    > Upload "."? (yes/no) yes
    
  2. The Coder CLI tool gives progress information then prompts you to confirm:

    > Confirm create? (yes/no) yes
    
    The template-tour template has been created! Developers can provision a workspace with this template using:
    
    coder create --template="template-tour" [workspace name]
    
  3. In your web browser, log in to your Coder dashboard, select Templates.

  4. Once the upload completes, select Templates from the top to deploy it to a new workspace.

    Your new template, ready to use

Dashboard

  1. Create a .zip of the template files.

    • On Mac or Windows, highlight the files and then right click. A "compress" option is available through the right-click context menu.

    • To zip the files through the command line:

      zip templates.zip Dockerfile main.tf
      
  2. Select Templates from the top of the Coder dashboard, then Create Template.

  3. Select Upload template:

    Upload your first template

  4. Drag the .zip file into the Upload template section and fill out the details, then select Create template.

    Upload the template files

  5. Once the upload completes, select Templates from the top to deploy it to a new workspace.

    Your new template, ready to use

Next steps