Backstage Software Templates is a core feature of Backstage that lets engineers create new components through a simple guided form. It pulls a code skeleton, fills in variables, then publishes to your source control. It can push to GitHub or GitLab. It can also run custom actions to fit your environment. In short, it gives you a paved path to start new software fast with your standards built in.
The Software Templates plugin adds a Create flow to your portal. Pick a template. Answer a few questions. The plugin scaffolds a repo, sets up CI, registers in the catalog, and can trigger docs or infra steps. Use it to spin up microservices, libraries, websites, data jobs, or internal tools. Platform teams encode guardrails in one place. Engineers get a clean starting point that matches company practice.
American Airlines showcases the feature in their Runway portal. “The quick and easy way to create applications with AA’s organizational best practices baked in.”
Installation Instructions
These instructions apply to self-hosted Backstage only. To use this plugin on Roadie, visit the docs.
Install the frontend package
Run these in the repo root
yarn workspace app add @backstage/plugin-scaffolder
Mount the Create page route
Edit packages/app/src/App.tsx
import React from 'react';
import { Route } from 'react-router-dom';
import { ScaffolderPage } from '@backstage/plugin-scaffolder';
import { FlatRoutes } from '@backstage/core-app-api';
// keep your existing imports above
export const App = () => (
<AppProvider>
<AppRouter>
<Root>
<FlatRoutes>
{/* keep your existing routes */}
<Route path="/create" element={<ScaffolderPage />} />
</FlatRoutes>
</Root>
</AppRouter>
</AppProvider>
);
Add a sidebar link to the Create page
Edit packages/app/src/components/Root/Root.tsx
import React from 'react';
import { SidebarPage, Sidebar, SidebarItem, SidebarSpace } from '@backstage/core-components';
import AddCircleOutline from '@mui/icons-material/AddCircleOutline';
// keep your existing imports
export const Root = ({ children }: { children?: React.ReactNode }) => (
<SidebarPage>
<Sidebar>
{/* keep your existing sidebar items */}
<SidebarItem icon={AddCircleOutline} to="/create" text="Create" />
<SidebarSpace />
</Sidebar>
{children}
</SidebarPage>
);
Install the backend on the new backend system
Add the backend package
yarn workspace backend add @backstage/plugin-scaffolder-backend
Register it in packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
// keep your other backend plugins
backend.add(import('@backstage/plugin-app-backend'));
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-catalog-backend'));
// add the scaffolder backend plugin
backend.add(import('@backstage/plugin-scaffolder-backend'));
backend.start();
You do not need to wire actions by hand in this setup. The plugin provides a standard set of actions.
Install the backend on the old backend system
Add the backend package
yarn workspace backend add @backstage/plugin-scaffolder-backend
Create packages/backend/src/plugins/scaffolder.ts
import { Router } from 'express';
import { PluginEnvironment } from '../types';
import { CatalogClient } from '@backstage/catalog-client';
import { ScmIntegrations } from '@backstage/integration';
import {
createRouter,
createBuiltinActions,
} from '@backstage/plugin-scaffolder-backend';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const catalogClient = new CatalogClient({ discoveryApi: env.discovery });
const integrations = ScmIntegrations.fromConfig(env.config);
const actions = createBuiltinActions({
integrations,
catalogClient,
reader: env.reader,
config: env.config,
});
return await createRouter({
logger: env.logger,
config: env.config,
database: env.database,
reader: env.reader,
catalogClient,
actions,
identity: env.identity,
});
}
Wire it into packages/backend/src/index.ts
// keep your existing imports
import scaffolder from './plugins/scaffolder';
// inside the main bootstrap function where you set up the routers
const scaffolderEnv = useHotMemoize(module, () => createEnv('scaffolder'));
apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv));
Optional sample template for local testing
Place a template file at templates/example/template.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: example-template
title: Example Template
description: A minimal template that logs a value
spec:
owner: [email protected]
type: service
parameters:
- title: Fill in values
properties:
name:
title: Name
type: string
description: A value to log
steps:
- id: show
name: Show input
action: debug:log
input:
message: 'Hello ${{ parameters.name }}'
Add a local file location in app-config.yaml so the catalog can load it
catalog:
locations:
- type: file
target: templates/example/template.yaml
rules:
- allow: [Template]
Now the Create page can show this template.
Set up Backstage in minutes with Roadie
Focus on using Backstage, rather than building and maintaining it.