Scaffolder Template Design

Scaffolder Template Design serves as the foundational architecture for standardizing service creation across modern developer portals. By defining reusable, parameterized workflows, platform teams can enforce architectural guardrails while accelerating developer onboarding. Effective template design integrates seamlessly with the broader Plugin Ecosystem & Custom Extensions, ensuring that automated provisioning aligns with organizational compliance, infrastructure standards, and team-specific operational requirements.

Scaffolder action sequence from inputs to catalog User parameters flow through fetch:template, publish:github and catalog:register, with each step passing output references to the next. Parameters JSON Schema fetch:template render skeleton publish:github repoContentsUrl catalog:register entity live each step's output feeds the next via steps['id'].output
A template runs as an ordered action chain: parameters render a skeleton, the repo is published, then the component is registered in the catalog.

Prerequisites

Before architecting a new template, verify that your environment meets core operational requirements. Platform engineers must establish a secure, auditable baseline for template execution and entity registration.

  • Repository Access & Credentials: Configure provider-specific tokens via environment variables (GITHUB_TOKEN, GITLAB_TOKEN, BITBUCKET_TOKEN). Store secrets in a centralized vault (e.g., HashiCorp Vault, AWS Secrets Manager) and inject them into the Backstage backend via app-config.yaml.
  • RBAC & Template Visibility: Implement role-based access control using the @backstage/plugin-permission-backend. Define policies that restrict template visibility to authorized groups (catalog:read, scaffolder:template:use).
  • Software Catalog Schema Alignment: Ensure your catalog-info.yaml schema matches the target entity types (Component, API, System). Validate against the backstage.io/v1alpha1 specification.
  • CI/CD Runner Permissions: Provision OIDC workload identity or IAM roles for execution runners. Runners require repo:write, actions:write, and infrastructure provisioning permissions (e.g., AWS sts:AssumeRole, GCP iam.serviceAccountTokenCreator) to bootstrap pipelines without manual credential rotation.
  • CLI Toolchain: Install @backstage/cli@^0.26.0 and ensure yarn or npm is available for local dry-runs and schema validation.

Step-by-Step Configuration

The configuration phase focuses on structuring the YAML definition, defining input parameters, and chaining execution actions. Templates must be deterministic, idempotent, and strictly validated before deployment.

Core YAML Architecture & Parameterization

Start by declaring the apiVersion and kind: Template, followed by a spec block containing parameters and steps. Use JSON Schema validation (required, type, pattern, ui:widget) to enforce type safety and required fields for user inputs.

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: standardized-service-template
  title: Standardized Service Template
  description: Bootstraps a new service with CI/CD and catalog registration
  tags:
    - platform
    - scaffolder
spec:
  owner: platform-engineering
  type: service
  parameters:
    - title: Service Configuration
      required:
        - repoName
        - owner
        - targetEnvironment
      properties:
        repoName:
          title: Repository Name
          type: string
          pattern: "^[a-z0-9-]+$"
          ui:autofocus: true
        owner:
          title: Team Owner (Group)
          type: string
          ui:field: EntityPicker
          ui:options:
            allowedKinds:
              - Group
        targetEnvironment:
          title: Deployment Environment
          type: string
          enum:
            - staging
            - production
          ui:widget: radio
  steps:
    - id: fetch-base
      name: Fetch Base Template
      action: fetch:template
      input:
        url: ./template
        values:
          repoName: ${{ parameters.repoName }}
          environment: ${{ parameters.targetEnvironment }}
          githubOrg: ${GITHUB_ORG}

Action Sequencing & Custom Extensions

Chain actions sequentially to handle repository creation, file templating, and initial CI/CD pipeline generation. Each step must output artifacts required by subsequent actions using the output field. When extending default capabilities, consider Building Custom Backstage Plugins to inject proprietary provisioning logic directly into the scaffolder pipeline.

    - id: create-repo
      name: Create Repository
      action: publish:github
      input:
        repoUrl: github.com?owner=${GITHUB_ORG}&repo=${{ parameters.repoName }}
        defaultBranch: main
        description: "Auto-provisioned service for ${{ parameters.owner }}"
        topics:
          - backstage-scaffolded
          - ${{ parameters.targetEnvironment }}
        protectDefaultBranch: true

    - id: register-catalog
      name: Register Component
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps['create-repo'].output.repoContentsUrl }}
        catalogInfoPath: /catalog-info.yaml

Validation

Rigorous validation prevents deployment failures and ensures catalog consistency. Implement automated checks at both the developer workstation and CI pipeline levels.

Dry-Run Execution & Schema Linting

Implement dry-run execution to verify parameter resolution, action sequencing, and file generation before publishing. Cross-reference the generated catalog-info.yaml against established Catalog Integration Patterns to guarantee seamless entity registration, ownership mapping, and metadata propagation.

# catalog-info.yaml (Injected during scaffolding)
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: ${{ parameters.repoName }}
  description: Auto-generated service component
  tags:
    - scaffolder
    - automated
    - ${{ parameters.targetEnvironment }}
spec:
  type: service
  lifecycle: experimental
  owner: ${{ parameters.owner }}
  system: platform-core
  dependsOn:
    - resource:platform-db-cluster

CI Pipeline Integration

Integrate automated schema linting and template parsing checks into your CI pipeline to catch structural regressions early.

#!/usr/bin/env bash
set -euo pipefail

# 1. Validate template syntax and JSON schema compliance
npx @backstage/cli catalog validate --path ./templates/service-template.yaml

# 2. Lint the template YAML structure
yamllint ./templates/service-template.yaml

# 3. Verify generated catalog entity (after a local dry-run)
cat ./dry-run-output/catalog-info.yaml | yq '.spec.type' | grep -q "service" && echo "Catalog schema valid"

Debugging & Deployment Verification

  • Log Inspection: Enable scaffolder debug logging via LOG_LEVEL=debug in the Backstage backend. Monitor action:fetch:template and action:publish:* execution traces.
  • Artifact Inspection: After dry-run, verify ./dry-run-output/ contains all expected files. Check for unrendered ${{ parameters.* }} placeholders.
  • Deployment: Commit validated templates to a dedicated Git repository ([email protected]:org/backstage-templates.git). Configure Backstage catalog.locations to reference this repository.

Maintenance

Template maintenance requires a structured versioning strategy and backward compatibility planning. Use semantic versioning for template releases and maintain a clear deprecation policy for legacy schemas. Monitor usage metrics to identify underutilized parameters and refactor complex workflows into modular sub-templates. For language-specific implementations, refer to Writing custom scaffolder templates for Node.js services as a reference for structuring dependency resolution, runtime configuration, and automated testing hooks.

Versioning & Rollback Procedures

  • Version Control: Tag template directories using Git tags (v1.2.0). Reference specific versions in Backstage via the ref query parameter on catalog location URLs (e.g., ?ref=v1.2.0).
  • Rollback Strategy: If a template update causes pipeline failures:
    1. Revert the Git tag to the last stable commit.
    2. Update your catalog location to reference the stable ref.
    3. Restart Backstage backend pods to clear cached template definitions.
    4. Manually clean up orphaned repositories using gh repo delete <org>/<repo> and remove stale catalog entities via the Backstage API (DELETE /api/catalog/entities/by-uid/<uid>).

Monitoring & Refactoring

Track template execution success rates, average completion time, and parameter usage distribution. Decompose monolithic templates into reusable fetch:template sub-modules. Implement conditional steps using if: ${{ parameters.targetEnvironment === 'production' }} to reduce branching complexity.

Common Pitfalls

Pitfall Mitigation Strategy
Overloading templates with environment-specific logic Leverage parameterization, conditional steps, and dynamic file templating (fetch:template with values mapping).
Failing to validate catalog-info.yaml generation Implement CI linting (@backstage/cli catalog validate) and dry-run assertions to prevent orphaned entities.
Hardcoding repository URLs or branch names Always use ${{ parameters.* }} interpolation and ${ENV_VAR} placeholders for host/org resolution.
Neglecting RBAC scoping Enforce catalog:read and scaffolder:template:use permissions via the permission framework; audit template visibility quarterly.
Skipping dry-run validation Mandate local dry-run testing in PR checks before merging template updates.

Frequently Asked Questions

How do I enforce mandatory security checks within a scaffolder template?

Add a step that runs a security scanner before the repository is published. Use the fetch:template step to stage files, then execute scanning before publish:github:

- id: security-scan
  name: Security Linting
  action: fetch:plain:file
  input:
    url: https://github.com/org/security-scripts/blob/main/scan.sh
    targetPath: ./scan.sh

For simpler cases, add a CI check to the scaffolded repository’s default workflow via a template file rather than as a scaffolder step.

Can scaffolder templates be versioned independently of the Backstage core?

Yes. Templates are stored as YAML files in a Git repository and can be tagged using semantic versioning. The Backstage catalog references specific template versions via the location URL ref parameter, allowing teams to roll out updates without breaking existing workflows.

What is the recommended approach for handling multi-environment deployments in templates?

Define environment-specific parameters (e.g., targetEnvironment, region, clusterName) and use conditional action steps or dynamic file templating to inject environment-appropriate configurations. Avoid hardcoding infrastructure endpoints; instead, resolve them via a centralized configuration service or parameter lookup. Use fetch:template with environment-specific directories (./template/staging/, ./template/production/) selected via if conditions in the steps array.