Building a development golden path with the Scaffolder: understanding the structure of a YAML template containing metadata, parameters, steps, and output, using user-value placeholders in the form schema, and assembling a simple scaffolding workflow with built-in actions such as fetch, publish, and register.

In episode 5, you saw how catalog ingestion and processing turns raw catalog-info.yaml files into living catalog entities, complete with processors and relations between entities. Episode 6 shifts the focus from recording what exists to creating something new: we dive into Software Templates, commonly known as the Scaffolder. This is where Backstage changes from a passive directory into a tool that actively builds. With one template, you replace dozens of wiki pages about how to start a new component — and everyone starts from the same point, with the same standards.
The golden path is a term for one official way to create and develop a component. That doesn't mean all components must be identical — parameters allow variations — but the infrastructure around a component is always standardized. Every new component born through the golden path already carries the correct folder structure, ready-to-use CI configuration, and a catalog entry, without needing to be reminded one by one.
The Scaffolder realizes this golden path through templates: a special catalog entity (kind Template) that describes what will be created, what questions will be asked of the user, and what sequence of steps will run to create it.
A YAML template is made up of four main parts:
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: example-react-service
title: Contoh Service React
description: Golden path untuk service React sederhana
tags:
- react
- service
spec:
owner: group:platform-team
type: website
parameters:
- title: Informasi Dasar
properties:
name:
type: string
title: Nama Service
description: Nama unik untuk service ini
steps:
- id: fetch-base
name: Ambil Base Template
action: fetch:template
input:
url: ./templates/react
values:
name: ${{ parameters.name }}
output:
links:
- title: Repository Service
url: https://github.com/example/${{ parameters.name }}Notice the block above: parameters defines one field named name, the fetch-base step calls the fetch:template action, and output produces a link to the repository. We'll dissect all of these parts one by one.
The parameters section follows JSON Schema. Backstage automatically translates it into a form in the UI: each property becomes one field, the data type determines the input kind (string, integer, boolean, array, object), and title plus description become the label and helper text. Not a single line of frontend code needs to be written for this form — the scaffolder backend builds it.
parameters:
- title: Detail Service
required:
- name
properties:
name:
type: string
title: Nama Service
minLength: 3
repoUrl:
type: string
title: Lokasi Repository
description:
type: string
title: Deskripsi
description: Tidak wajib, tetapi sangat disarankanThe values a user fills in the form are available as placeholders during template execution. Placeholders use a special scaffolder syntax, and because they contain a dollar sign and curly braces, they may only live inside code blocks — in the example above you see them in the values and output sections. When the user fills the name field with payment-api, every occurrence of that placeholder is replaced with payment-api when the step executes.
To control the order and behavior of the form, the scaffolder supports properties prefixed with ui: such as ui:order, ui:autofocus, ui:help, and ui:widget — all of them polish the form-filling experience without writing a manual React component.
The steps section is a list of steps executed in order. Each step has a unique id, a name shown in the UI, an action that determines which action is invoked, and input containing the parameters for that action. The order in the list is the execution order — the second step only begins after the first finishes.
The output section defines what is presented to the user after all steps complete. The most common forms are links pointing to the repository, pipeline, or documentation, and text as a summary message. There's also entities for entities to be registered into the catalog — a concept deepened in episode 10.
The Scaffolder ships with a collection of built-in actions ready to use. An action is the smallest unit of work — one step calls one action. The most important ones for beginners:
| Action | Function | Common use |
|---|---|---|
fetch:template | Copies a template folder into the workspace while replacing placeholders | Starting repository contents from a base template |
publish:github | Creates a new repository on GitHub and pushes the result there | Getting a fresh GitHub repository ready to work on |
catalog:register | Registers an entity into the Backstage catalog | Adding the new entity produced by scaffolding |
debug:log | Prints a message to the scaffolder logs | Tracing parameter values while debugging |
debug:wait | Delays execution for a set amount of time | Simulating a process that takes time |
debug:create-file | Creates a file from inline content | Making small config files without a template folder |
Beyond these six there are many more actions for GitLab, Azure DevOps, Kubernetes, and the rest of the debug:* family — but these six are enough of a foundation to understand the Scaffolder flow.
The simplest and most common scaffolding workflow consists of three sequential steps: fetch the base template, publish to GitHub, then register the entity into the catalog. Assemble them all in one steps:
steps:
- id: fetch-base
name: Ambil Base Template
action: fetch:template
input:
url: ./templates/react
values:
name: ${{ parameters.name }}
owner: ${{ parameters.owner }}
- id: publish
name: Publikasikan ke GitHub
action: publish:github
input:
repoUrl: ${{ parameters.repoUrl }}
defaultBranch: main
- id: register
name: Daftarkan ke Catalog
action: catalog:register
input:
repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
catalogInfoPath: /catalog-info.yamlNotice how the register step takes the output of the publish step via steps.publish.output.repoContentsUrl — a step's output can become the input of the next step. This is how data flows through the pipeline.
Before it can be used from the UI, the template itself must be registered in the catalog as a Template entity. The process is the same as registering a regular component: point to the location of the file containing the Template entity.
curl -X POST "http://localhost:7007/api/catalog/locations" \
-H "Content-Type: application/json" \
-d '{"target": "https://github.com/example/backstage-templates/blob/main/templates/react-service/template.yaml"}'Once registered, the template appears on Backstage's Create page, and users just press a button, fill in the form, and wait for the pipeline to finish.
Tip
Start with a simple template containing one fetch:template step, then add publish and register once the flow feels comfortable. The fastest way to debug the scaffolder is to insert a debug:log step between the problematic steps — you'll see parameter values exactly as the scaffolder holds them.
In this episode 6, you built the Scaffolder foundation: the golden path concept and how templates realize it, the anatomy of a YAML template with metadata, parameters, steps, and output, form schemas that automatically become forms, user-value placeholders, built-in actions like fetch:template, publish:github, catalog:register, and debug:*, and the most common three-step scaffolding workflow.
The key takeaways:
Template entity in the catalog.debug:* actions are your debugging friends — use debug:log to see the values the scaffolder actually holds.In the next episode, episode 7, we build a documentation layer on top of the components you scaffold: TechDocs — how Backstage makes documentation part of the workflow, built directly from source, and always in sync with the code.