What a Backstage software template does and when to write one
A template.yaml defines a parameter form, an ordered list of actions that run when a developer submits it, and the output links shown on success. Platform teams write one when the same kind of request keeps arriving, such as a new service repository, a standard pipeline or a starter application, and each request currently takes a person several steps to fulfill by hand. In Backstage, Software Templates is one of the core built-in features listed in the architecture overview, next to the Software Catalog and TechDocs. A template is registered as a Template entity in the catalog, so developers find it in the same place they find the services it creates, and the catalog records who owns it.
Templates fit best when the output is standard enough to describe in advance: the same repository layout, the same CI configuration, the same catalog descriptor and the same ownership metadata every time. They also fit when the request is frequent enough that the time spent answering tickets is worth automating. They fit poorly when every request is a one-off design decision, when the output changes often enough that the template would be wrong most of the time, or when the team has not yet agreed on a standard. In those cases a written checklist or a short consultation is more honest than a form that promises a result it cannot deliver. Before writing anything, confirm which Git platform, identity provider and cloud accounts the template will touch, because each one adds credentials, permissions and review steps that shape the design.
Template structure options and when each fits
This table compares the main choices for parameter forms, step patterns and output links in a Backstage software template. Read each row for the situation where the option fits and the upkeep it adds, since every extra field, step or link becomes something your team must test and keep current. The main takeaway is that a smaller template with fewer parameters and steps is usually cheaper to maintain than one that tries to cover every team's case.
| Option | When it fits | Maintenance cost to plan for |
|---|---|---|
| Single-page parameter form | Few inputs, one audience, and a template that creates one kind of component | Low. One form to review when the skeleton or naming rules change |
| Multi-step parameter form | Many inputs grouped by concern, such as repository placement, ownership and cloud settings | Moderate. Each step needs validation, and conditional steps add paths to test |
| Free text field | Values that cannot be listed in advance, such as a service description | Moderate. Expect inconsistent values that later steps or catalog queries must handle |
| Enum or picker field | Values drawn from a known set, such as approved languages, regions or owning groups | Moderate to high. The list must be kept in sync with the real options, and pickers that read live data depend on those sources staying available |
| Required parameter | Values the steps cannot run without, such as the component name or owner | Low. Few paths to test, but every required field adds friction for users |
| Optional parameter | Settings with a sensible default that some teams need to change | Moderate. Each optional value needs a defined behavior when it is empty, and each combination is a potential test case |
| Fetch only | Scaffolding a local skeleton for users to push themselves, or previewing output | Low. No repository credentials needed, but users must complete the publishing step manually |
| Fetch and publish | Creating a repository with the skeleton already in it, without a catalog entry | Moderate. Requires publishing credentials and platform-specific settings to be kept current |
| Fetch, publish and register | The standard path for a new service that should appear in the catalog straight away | Higher. Adds a registration step and catalog file rules that must match the skeleton |
| Output link to repository | Teams need to open the new code immediately after a run | Low. Depends on the publishing step returning a stable URL |
| Output link to catalog entry | Teams need to find ownership, docs and dependencies after creation | Low to moderate. Registration must succeed before the link is shown |
| Output link to CI pipeline | The new repository starts builds or deployments on creation | Moderate. Pipeline URLs change with CI platform and project layout, so confirm the link format for your platform |
| Output link to documentation | Teams should read setup guidance before writing code | Low. Confirm the documentation path exists in the skeleton before linking to it |
Authoring a template.yaml step by step
This sequence takes a template from an empty file to a registered Software Template that a developer can find in the catalog, fill in, and run. Each step ends with a check you can perform before the template reaches a single team.
Create the template file in a catalog repository
Create a template.yaml file in a Git repository that your catalog already reads, normally a dedicated templates repository owned by the platform team. Set apiVersion to scaffolder.backstage.io/v1beta3 and kind to Template, then give the metadata a name, a title, a short description, and tags. The name is the identifier teams will see in URLs and logs, so choose it carefully before anything depends on it. Confirm the file parses as YAML and that the apiVersion matches the scaffolder version in your Backstage release, since template schema versions change across releases. Check the current value in the Backstage documentation for your version before you copy it from older examples.
Define parameters with JSON Schema validation
Under spec.parameters, group the questions a developer answers into sections, each with a title and a JSON Schema-style properties block. Mark required fields, set types, and use constraints such as enumerations, patterns, and string length limits to reject bad input before any action runs. Use ui:field, ui:help, and ui:placeholder hints to shape the form, and keep each section short enough that a developer can finish it in one pass. Validate the template with your catalog tooling, then open the template in the Create page and check that required fields block submission and that the field order matches the order a developer thinks in.
Order the steps for fetch, variables, and publish
Under spec.steps, arrange the actions in the order they must run. A typical sequence fetches the skeleton with fetch:template, then creates the repository or project with a publish action for your Git platform, and finally registers the new component. Each step has an id, a name, an action, and an input block that references parameters with the ${{ parameters.name }} syntax. Give each step an id so later steps and the output section can refer to its results. Review the order against the dependencies: a publish step needs the rendered skeleton, and a register step needs the repository URL that publish returns. Confirm the order by running the template in dry-run mode if your Backstage version supports it, and read the task log for each step.
Render skeleton files with template variables
Place the skeleton files in a directory next to template.yaml and use the Nunjucks syntax in filenames and file contents to insert parameter values, such as a service name or owning team. Keep skeleton files free of secrets and environment-specific values, since every new project copies them. Add a catalog-info.yaml file to the skeleton so the registered component carries its owner and system from the first commit. Check the output by previewing the rendered files in a dry run or in a throwaway project, and confirm that names, owners, and annotations match what the developer entered. Any mistake here is copied into every project created from the template, so review this output with a second person.
Publish the rendered project to Git
Add the publish action that matches your source control platform, such as the GitHub, GitLab, Azure DevOps, Bitbucket, or Gitea action module, and supply the repository location from a parameter or from a fixed organization setting. Set the repository visibility, default branch, and any required settings your policy demands. Credentials belong in the Backstage configuration as secrets, not in template.yaml. Confirm the result by opening the new repository and checking that the skeleton files, the catalog-info.yaml file, and the default branch are present, and that the action output includes a repository URL the next step can use. Platform-specific details, such as token scopes and branch protection, are covered on the matching publishing pages.
Register the new component in the catalog
Add a catalog:register action that points at the catalog-info.yaml file in the new repository, using the URL returned by the publish step. Registration makes the component appear in the catalog without waiting for the next scheduled ingestion, which lets developers open their new service the moment the run finishes. Confirm the registration by searching the catalog for the component name and checking that its owner, system, and lifecycle values match the parameters. If the entity does not appear, check that the repository location is readable by the catalog and that the descriptor is valid. Ask your catalog owners which registration behavior applies to your deployment, since it depends on how your catalog locations are configured.
Wire the output links shown on success
Under spec.output, add links that the developer sees when the run succeeds. Use the step outputs to link to the new repository, the catalog entity page, and any pipeline or documentation page the team needs next. Use the expression syntax to reference step outputs by their ids, and keep the link titles short and specific so the developer knows which link to open. Run the template once and confirm that every link resolves, opens the correct page, and appears on the completion screen. A broken link on success is one of the most visible defects a template can ship, so test it before you publish the template.
Register the template and confirm it appears
Add the template to the catalog through a location your catalog reads, then open the Create page and confirm that the template appears under the expected tags and title. Run the template end to end in a non-production organization or sandbox, and check the parameter form, the generated repository, the catalog entry, and the output links against the expected results. Ask your platform owner who may register or change Template entities, because that decision affects who can publish templates to every developer. Once the end-to-end run passes, announce the template to its users with the version, the parameters they must supply, and the contact for problems.
How parameter forms and validation protect the platform
Required fields, enums and pattern checks stop bad input before any repository exists. In a template's parameters block, each field can be marked required, given a fixed set of choices through enum, and constrained with a pattern regular expression. Those rules run in the browser form before the first step starts, so a malformed service name or an unknown region never reaches the publish step. Repositories and webhooks are much harder to clean up than a rejected form, so every rule you add moves a failure from the most expensive point in the workflow to the cheapest one. Confirm how your scaffolder version renders each keyword, including whether pattern and enum are enforced on the server side as well as in the form, because a template can be submitted through the API without the UI.
Naming and ownership fields should map directly to catalog entities. The owner field should be a picker that resolves to a Group or User entity, not free text, and the component name should follow the same naming rules the catalog uses for entity names. If system or owner accepts any string, the new component registers under a system that does not exist, or under an owner nobody can find, and it becomes an orphan from its first day. Strict validation has a cost: a team that cannot find its group in the picker will stop using the template or work around it. The practical balance is strict rules for identifiers the platform depends on, and clearly worded, optional fields for descriptive text that does not affect discovery or ownership.
Confirm the validation rules against your scaffolder version before you rely on them. Field types, picker widgets, custom validators and how errors appear differ between Backstage releases, and the upgrade notes for each release are the place to check changes. Run a deliberately invalid submission in a test environment for every rule, including one sent directly to the scaffolder API, and record what the user sees. Keep those cases with the template so that a Backstage upgrade re-runs them. Review the form with a team lead who did not write the template, since the labels and help text they read are the controls most people actually see.
Where templates fail in practice
Most template failures show up after a developer clicks Create, not during authoring. The six cards below cover the failures we see most often in template.yaml files, and each one names the symptom a team notices first and the change to make in the template.
Unrendered variables in skeleton files
Skeleton files contain placeholders such as ${{ values.name }} that never get replaced, so the new repository ships with literal template syntax in its README, package names and manifests. The usual cause is a fetch:template step that points at the wrong targetPath or a file with a different extension than the templating engine expects. Check the generated output of a dry run for any remaining ${{ strings, and confirm every skeleton file is inside the path the fetch step renders.
Steps running out of order
A publish step that runs before the skeleton is fetched creates an empty repository, and a register step that runs before publish has no URL to point at. Symptoms include catalog entries with broken source locations and repositories with no commits. Order the steps as fetch, then publish, then register, and make each later step read outputs from an earlier step by its id so a reordering fails visibly instead of silently.
Publish succeeds, registration fails
The repository is created, then the catalog registration call is rejected, for example because the catalogInfoPath file is missing or the entity name already exists. The developer sees a failed run with a live repository that nobody owns in the catalog. Register the component only after the publish step returns a repository URL, and decide in advance whether a failed registration should be retried or cleaned up, because the template will not do that for you.
Missing owner values
When the owner parameter is optional or defaults to an empty string, the registered component has no owner and drops out of ownership reports. Teams then discover orphaned services during an audit or incident. Make owner a required parameter that uses an entity picker restricted to Group kinds, and pass that value through to the catalog-info.yaml skeleton so the owner is set in the descriptor from the start.
Outputs pointing to wrong URLs
The links shown on the success page can point at a default branch, a stale host, or the wrong repository when a step output is built by hand. Developers then open a 404 or a repository they did not create. Build output links from the publish step's returned remoteUrl and the catalog entity reference, not from parameters typed by the user, and open each link once in a dry run before releasing the template.
Parameters drifting from naming standards
Free-text parameters let teams enter names that break the organization's standard, such as uppercase letters, spaces or a service name that conflicts with an existing component. The result is inconsistent repository names and catalog entries that are hard to search. Use pattern and enum constraints in the parameter schema, derive the repository name from a single validated field, and keep the allowed values in one place so the naming rule can be updated across templates together.
How long does template authoring take and what does it cost to outsource?
How long does it take to author a Backstage software template?
No duration is supplied for this work, so no reliable timeline can be given here. The time depends mainly on how many parameters the form needs, how many publish targets the template covers, whether custom actions must be written, and how many review rounds your platform and security teams require. A single template that creates one repository on one Git platform with a standard skeleton is a very different job from a set of templates covering several services, languages and targets. Ask any partner to show the timeline as a breakdown by template and by review stage, tied to the written scope.
What drives the cost of outsourcing template authoring?
The main cost drivers are the number of parameters and their validation rules, the number of publish targets, whether custom actions are needed, and the number of review cycles. Each parameter adds form logic and test cases. Each publish target adds a separate action configuration, credentials and placement rules. Custom actions require design, typed input schemas, tests and documentation. Review cycles add time from both sides. Ask for a quote that lists each template, its parameters, its targets and its custom actions as separate line items so you can see which choices move the price.
What do we need to provide before a template engagement starts?
Before work starts, you should have a written naming and ownership standard for repositories, components and owner references, because templates depend on those rules to generate valid entities. You also need access to the Git platform the templates will publish to, with an account or token scope agreed with your security team, and a test organization or group where templates can create real repositories safely. Named reviewers for the platform team and the security team should be available on a fixed schedule. Confirm each of these items with your own environment before signing a scope.
Can our internal platform team author Backstage templates without a partner?
Yes, in many organizations an internal team can author templates, especially simple ones that use built-in actions and a standard skeleton. The skills needed are TypeScript or YAML proficiency, familiarity with the Backstage scaffolder and catalog, and the ability to test templates in a non-production organization. Custom actions, multi-platform publishing and access control design usually require deeper experience with the Backstage codebase. A partner can cover the parts your team has not built before, or review the templates your team writes, and your team keeps ownership of the result.
What does a fixed-scope quote for template work depend on?
DeployOpen works on fixed-scope engagements where scope, deliverables and timelines are agreed in writing before work starts. A quote follows from that written scope, so it depends on the templates in scope, their parameters and validation rules, the publish targets and credentials, any custom actions, the test and review process, and the handover material. DeployOpen does not publish a price list. Ask for the scope to name each template and each open question, so changes after kickoff can be priced as explicit change requests.
Is there ongoing cost after the templates are delivered?
Yes, templates need maintenance after delivery. Backstage releases, Node.js changes, Git platform API changes and new team requirements all affect templates over time. Upgrades to Backstage move every @backstage package together, so template actions and their dependencies must be checked on each upgrade. Ongoing work can be handled by your own team or by a retainer for support and upgrades. Ask any partner to state which parts of template maintenance are covered after handover and how change requests are priced.
What review process should we expect before templates reach developers?
A reasonable process has your platform team review the parameter form and step order, your security team review the credentials and repository settings, and a test run in a non-production organization confirm the output. Each reviewer should sign off in writing. Plan for at least one round of changes, since first drafts usually miss validation edge cases or naming rules. The number of rounds depends on how many templates are in scope and how clearly the standards were written before work started.
What to confirm in your environment before writing templates
A template runs inside a specific Backstage version, Node.js runtime, set of scaffolder actions and identity setup. Confirm these five points first, because each one decides which parameter types, steps and publishing actions you can use without rework.
Backstage release line in use
Confirm whether your instance follows the main release line, which ships monthly, or the next line, which ships weekly. Scaffolder actions and field extensions can change between releases, so a template tested on one line may fail on another. Record the exact version in your repository so the template author and the platform team test against the same build. Ask which line your upgrade plan assumes before you pin any action names.
Supported Node.js version
Backstage supports exactly two adjacent even-numbered Node.js releases, and as of Backstage 1.46.0 these are Node.js 22 and 24. Check the runtime on the host or container that runs the scaffolder, because custom actions execute there and a mismatch can break them at run time. Confirm the Node.js version in your build pipeline matches the one in production so the template passes the same checks in both.
Git platform and action modules installed
List which scaffolder action modules are installed for your source control platform, such as GitHub, GitLab, Azure DevOps, Bitbucket, Gitea or Gerrit. A step such as publish:github will not exist unless its module is present, and the action names differ between platforms. Confirm the credentials each module uses, and whether your organization expects a single Git host or several, before you design the publish step.
Scaffolder field types available
Check which parameter field types and custom field extensions your version provides, such as string, enum, boolean, arrays, owner pickers and repository pickers. Some pickers depend on a plugin or a catalog that is not present in every installation. Choose field types from this list rather than assuming them, and note any field you need that is missing so you can plan an extension or a plain text fallback.
Identity-to-owner mapping
Confirm whether your identity provider resolves each signed-in user to a catalog User entity, because owner fields and the entity created by a template depend on it. If sign-in maps users to a different identity, the owner default may point to nothing, and the registered component will have no accountable team. Ask the platform team which sign-in resolver is in use and whether groups are imported into the catalog before you wire owner parameters.
Rolling a template out to developers
This sequence moves a new template from the platform team's test repository to general use, with each step producing evidence that the form, the generated project and the documentation work for the teams that will run them. It gives the platform team a controlled way to fix problems while the audience is still small.
Choose a pilot team and scope
The platform team selects one team that has a real need for the new service type and agrees with that team which parameters, actions and target Git location the pilot will use. Write the scope down in the template's README or the pilot ticket so both sides agree what success means. The step is done when the pilot team has confirmed the scope and has a named contact who will report problems.
Run the template as the pilot team
A pilot member runs the template from the Create tab in the portal, filling in every parameter as a typical developer would, including the ones with defaults. The platform team watches the task log for failed steps and notes any field the pilot team hesitates over. The step closes when one full run completes and the output links open in the browser.
Collect feedback on form wording
Gather the pilot team's comments on labels, descriptions, placeholders and validation messages, and check each one against the template's parameter definitions. Change wording where a developer had to guess what a field expects, such as a naming format or an allowed value list. The step is done when the pilot team confirms that a new user could complete the form without asking for help.
Review the generated repository
The platform team opens the generated repository and checks the files, the catalog-info descriptor, the owner and lifecycle values, the CI configuration and any branch protection or repository settings applied by the publish step. Compare the result with the reference output recorded during testing. The step is done when the review lists no open defects, or every defect has an agreed fix that is applied and rerun.
Widen access to other teams
After the pilot passes, the platform team extends use to the next group of teams in agreed waves, and records who received access and when. Access rules for who may run the template are set separately, so this step follows the restriction approach described on the restriction page. The step is done when the wider group can see the template in the catalog and the platform team has a channel for questions and defect reports.
Document the template in TechDocs
Write a short TechDocs page next to the template that explains its purpose, each parameter, the resources it creates, the required permissions and the steps to follow after the run. Link the page from the template entity so users find it from the portal. The step is done when the documentation builds in TechDocs and a pilot team member confirms it answers their first questions.
Confirm the template is ready for general use
The platform team confirms that the template version in the catalog matches the reviewed version, that the owner has an active support route and that the feedback channel is listed in the documentation. Record the release in the team's change log so later edits can be traced to a version. The step is done when the template is announced to the wider audience with its documentation link.
Can our own team write a Backstage template without a partner?
Can a platform engineer who knows YAML write a basic Backstage template alone?
Yes, a platform engineer comfortable with YAML, JSON Schema style parameter forms and your Git platform can usually author a basic template that fetches a skeleton, fills in values and publishes a repository. The template.yaml file is declarative, and the built-in steps cover common cases. Expect the first version to take iteration: the parameter form, the step order and the output links all need testing against a real catalog before developers rely on them. Confirm that someone on your team can run a local Backstage instance and read the scaffolder task logs, since most early debugging happens there.
What skills does a custom scaffolder action require?
A custom action is a TypeScript module, so the team needs working knowledge of Node.js, the Backstage backend plugin model and the createTemplateAction API with its input schema. Someone must also be able to register the action through a backend module, write unit tests for its handler and document it for template authors. Built-in actions cover many needs, so the question is usually whether your requirement justifies that ongoing ownership. Ask yourself who will update the action when Backstage upgrades change plugin interfaces.
How do we know if our template needs a custom action?
Start by listing every step the template must perform and checking each against the built-in actions in your Backstage version. If a step calls an internal API, a ticketing system or a cloud service that no action supports, you need a custom action or a fetch-and-HTTP workaround. Mark the steps that touch credentials or create external resources, because those are where a custom action needs the most review. Keep the list in your design notes so the same decision is not repeated for each new template.
When does a partner add real value to template work?
A partner tends to add the most value when the template must publish to more than one Git platform, create repositories with branch protection, pipelines and catalog registration in one run, or follow a review process that your own team has not yet defined. Outside help is also useful for handover, where the goal is that your engineers can change templates, test them with dry runs and answer questions after the engagement ends. Most DeployOpen engagements are fixed-scope, so the written scope should name the platforms, actions and handover materials before work starts.
What does a partner engagement for templates cost, and what drives it?
DeployOpen does not publish a price list, and a quote follows from the written scope. The main cost drivers are the number of templates, the number of Git platforms and whether custom actions are needed, along with the credential and review model your organization requires. Ask for the quote to separate one-time build work from any retainer for ongoing template changes and Backstage upgrades. Also ask who maintains actions after handover, because that work recurs with each Backstage release line.
How long does it take an internal team to build its first template?
Duration depends on the team's Backstage experience, the number of parameters and steps, and how much of the Git platform setup already exists. A team with prior Backstage work will move faster than one learning the framework alongside the template. Plan for time to test in a non-production instance and to gather feedback from the teams who will use the form. Your project plan should treat the first template as a learning exercise and the second as the first reusable pattern.
Which risks should our team check before publishing its own templates?
Check who can run each template, because a template that creates repositories or cloud resources runs with the credentials it is given. Confirm the scaffolder's token scopes are minimal, that secrets are not echoed in logs or output links, and that a dry run has been reviewed before publishing. Also verify that the Git platform account the template uses has only the rights needed for placement. Keeping an owner and a review step for each template reduces the chance of an unreviewed change reaching every developer.
How DeployOpen scopes and delivers template work
DeployOpen approaches Backstage software templates as a planned build: parameters, steps and outputs are agreed in writing before any template code is written. The work runs inside your own infrastructure and source control, and the finished template is handed over with documentation your team can maintain.
How we scope
- Written scope lists the parameter form, the step order and the Git platform the template publishes to.
- Scope names the skeleton repositories, owners and naming rules the template must respect.
- Engagements are fixed-scope, so deliverables and timelines are agreed in writing before work starts.
How we build
- Parameter forms are built with validation rules that match your naming and ownership standards.
- Steps are ordered so the skeleton is fetched, published and registered in the catalog as one flow.
- Each template is tested in a non-production Backstage instance before it reaches developers.
How we hand over
- Handover includes the template.yaml, skeleton sources and the success output links your teams see.
- Documentation explains each parameter, its validation and the steps a developer can expect to run.
- Your engineers receive a walkthrough of how to change parameters, steps and outputs safely.
How we support
- Ongoing support, upgrades and continuous testing are available as a retainer after handover.
- Retainers can cover template updates when skeletons, Git platform settings or Backstage versions change.
- Confirm with DeployOpen which Backstage version and Node.js release your template must support.

