TT Lab
Get started
Learn Learning paths Courses

CBA — Backstage Associate

Writing a Software Template and TechDocs

Continue in TT Lab

Goal

Write the three layers of a Backstage software template yourself — parameters, steps, and output — and create the mkdocs configuration for TechDocs and a skeleton entity. At the end, you put the workload that template would generate onto a real cluster and check the result.

Why it matters

The value of the scaffolder is not reducing typing but making the verified path the default. If you harden the traps the platform team has stepped on once — needing a particular CRD version, or having to use a particular workaround — into the skeleton and documentation, other people do not step on them again. And the structure itself matters. Because parameters is a JSON Schema, wrong input is blocked at the form stage, and because steps is a combination of reusable actions, you do not have to rewrite the repository creation and catalog registration parts when you build a new template. One more easily missed point — what runs publish:github is not the user's browser but the Backstage backend. Because the token is only on the server, self-service through the portal is safer than handing out tokens to every developer. Backstage is not in this environment, so you write the template as a file and grading reads the files, and only the last step uses a real cluster.

Steps

  1. Write /root/cba-template/template.yaml — apiVersion: scaffolder.backstage.io/v1beta3, kind: Template, metadata.name: node-service, metadata.title any value, metadata.tags with the first item nodejs, spec.owner: group:team-platform, spec.type: service.
  2. In the same file, add spec.parameters — the first item of the array has title (any value), required as [name, owner], properties.name as type: string with pattern: '^[a-z0-9-]+$' and title, and properties.owner as type: string with title.
  3. In the same file, add spec.steps — exactly 3, in order: id: fetch (action fetch:template, input.url: ./skeleton, and in input.values.name a substitution expression for the name parameter), id: publish (action publish:github), and id: register (action catalog:register, in input.repoContentsUrl a reference to the publish step's output, and input.catalogInfoPath: /catalog-info.yaml).
  4. In the same file, add spec.output — in the first item of links, title is Repository, url is a reference to the publish step's remoteUrl output, and entityRef is a reference to the register step's entityRef output.
  5. Write /root/cba-template/mkdocs.yml — site_name any value, the first item of nav is Home: index.md, and the first item of plugins is techdocs-core. Then in /root/cba-template/docs/index.md, write one title line that starts with # and a description paragraph.
  6. Write /root/cba-template/skeleton/catalog-info.yaml — kind: Component, metadata.name as a template value substitution expression (the string must contain values.name), metadata.annotations with backstage.io/techdocs-ref: dir:. and backstage.io/kubernetes-id: node-service, spec.type: service, spec.lifecycle: experimental, and spec.owner as a substitution expression for the owner value.
  7. Put the result the template would generate onto the cluster. Create the namespace cba-scaffold, and inside it create the Deployment node-service (labels backstage.io/kubernetes-id: node-service and app.kubernetes.io/part-of: cba-platform, image node:22-alpine, replicas 2, and the same backstage.io/kubernetes-id in the Pod template labels too), the Service node-service (port 80, targetPort 3000), and the ConfigMap node-service-techdocs (the value of the key techdocs-ref must be exactly the same as the annotation value of the skeleton from step 6).

Notes

Template manifest skeleton

Write /root/cba-template/template.yaml — apiVersion: scaffolder.backstage.io/v1beta3, kind: Template, metadata.name: node-service, metadata.title any value, metadata.tags with the first item nodejs, spec.owner: group:team-platform, spec.type: service.

A Template has a different apiVersion from catalog entities. Use the group dedicated to the scaffolder, and the owner has the same reference format as other entities.

parameters — the user form

In the same file, add spec.parameters — the first item of the array has title (any value), required as [name, owner], properties.name as type: string with pattern: '^[a-z0-9-]+$' and title, and properties.owner as type: string with title.

parameters is an array of pages, and each page is a JSON Schema. Check where the list of required items and the property definitions go.

steps — a combination of actions

In the same file, add spec.steps — exactly 3, in order: id: fetch (action fetch:template, input.url: ./skeleton, and in input.values.name a substitution expression for the name parameter), id: publish (action publish:github), and id: register (action catalog:register, in input.repoContentsUrl a reference to the publish step's output, and input.catalogInfoPath: /catalog-info.yaml).

Each step has id, name, action, and input. Recall which action name corresponds to fetching the skeleton, creating the repository, and registering in the catalog.

output — what to show at the end

In the same file, add spec.output — in the first item of links, title is Repository, url is a reference to the publish step's remoteUrl output, and entityRef is a reference to the register step's entityRef output.

You reference the result of an earlier step through the step id. Think about which step's output gives the repository address and which gives the registered entity reference.

mkdocs configuration and documentation

Write /root/cba-template/mkdocs.yml — site_name any value, the first item of nav is Home: index.md, and the first item of plugins is techdocs-core. Then in /root/cba-template/docs/index.md, write one title line that starts with # and a description paragraph.

TechDocs uses mkdocs. The configuration file needs a site name, a navigation list, and the plugin for TechDocs.

The skeleton's catalog-info.yaml

Write /root/cba-template/skeleton/catalog-info.yaml — kind: Component, metadata.name as a template value substitution expression (the string must contain values.name), metadata.annotations with backstage.io/techdocs-ref: dir:. and backstage.io/kubernetes-id: node-service, spec.type: service, spec.lifecycle: experimental, and spec.owner as a substitution expression for the owner value.

It is a file the template will generate, so the name position takes a substitution expression, not a value. Do not forget the annotation that points to the documentation location.

Apply the generated result to the cluster

Put the result the template would generate onto the cluster. Create the namespace cba-scaffold, and inside it create the Deployment node-service (labels backstage.io/kubernetes-id: node-service and app.kubernetes.io/part-of: cba-platform, image node:22-alpine, replicas 2, and the same backstage.io/kubernetes-id in the Pod template labels too), the Service node-service (port 80, targetPort 3000), and the ConfigMap node-service-techdocs (the value of the key techdocs-ref must be exactly the same as the annotation value of the skeleton from step 6).

You put up the workload the template would generate, yourself. The documentation reference value you wrote in the skeleton and the value you put in the cluster must be the same.