← All writing

Backstage Software Templates: How I Scaffold Production-Ready Services

· Sourabh G Kulkarni

How Spotify Backstage software templates and the service catalog eliminate repetitive boilerplate, ensuring every microservice launches with verified CI/CD, observability, and clear ownership on day one.

Backstage Software Templates: How I Scaffold Production-Ready Services

When an engineering team builds a new service, they usually want to focus immediately on business logic and domain code. However, setting up a production-ready foundation from scratch requires significant upfront effort: configuring build tools, container files, continuous integration workflows, testing harnesses, observability hooks, and cloud infrastructure.

Without an automated baseline, teams often face common challenges:

  • Manually assembling pipelines leads to subtle configuration inconsistencies across teams.
  • Core requirements—like standardized health probes, metrics endpoints, and logging formats—have to be repeatedly re-implemented.
  • Determining service ownership and maintenance responsibility becomes harder as organizations scale.

Spotify Backstage Software Templates (the Scaffolder) solve this by turning organization standards into a repeatable, self-service Golden Path. With an approved template, an engineer selects their framework, enters essential project metadata, and immediately gets a fully configured repository with verified CI/CD, container definitions, observability endpoints, and registered service ownership.


The Value: Standards Without Friction

In modern software delivery, platform teams aim to reduce cognitive load rather than enforce burdensome checklists. Standardizing the onboarding workflow provides immediate advantages:

  • Day-One Observability: Every new service starts with pre-configured Prometheus metrics scraping, health checks (/healthz), and structured logging out of the box.
  • Consistent Delivery: Build pipelines follow audited security practices, automated testing runs by default, and multi-stage container builds prevent oversized artifacts.
  • Clear Ownership & Governance: Centralizing service metadata makes incident response straightforward—every component has an unambiguous owning team and clear runbooks attached.

As discussed in DevOps to platform engineering and golden paths without the buzzwords, platform engineering isn't about imposing restrictive mandates. It is about making the right operational path the easiest path.


Anatomy of a Backstage Software Template

Backstage Software Templates are declared in simple YAML manifests (template.yaml) stored directly in git. Each template is composed of three essential blocks: Parameters, Steps, and Output.

┌─────────────────────────────────────────────────────────────┐
│                 Backstage Template Workflow                 │
├─────────────────┬─────────────────────────┬─────────────────┤
│ 1. Parameters   │        2. Steps         │    3. Output    │
│ (Developer UI)  │  (Scaffolder Actions)   │  (Repo & Links) │
└────────┬────────┴────────────┬────────────┴─────────┬───────┘
         │                     │                      │
         ▼                     ▼                      ▼
    User inputs         Fetch skeleton →       Open PR / Repo
   (name, owner,         Render Cookiecutter   Register Component
      runtime)           Publish to GitHub      in Service Catalog

1. Parameters: Keep Forms Radical and Short

A fatal mistake platform teams make is turning a template into a 20-question questionnaire. If developers have to pick CIDR ranges, ingress classes, state buckets, and CPU quota sizes, they will abandon the portal and go back to cloning old repositories.

Keep the input form focused on application intent:

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: python-fastapi-service
  title: Python FastAPI Microservice
  description: Scaffold a production-ready Python service with Docker, CI/CD, and metrics
spec:
  owner: platform-team
  type: service

  parameters:
    - title: Service Details
      required:
        - component_id
        - description
        - owner
      properties:
        component_id:
          title: Service Name
          type: string
          description: Unique kebab-case name of the service
          ui:field: EntityNamePicker
        description:
          title: Description
          type: string
        owner:
          title: Owning Team
          type: string
          ui:field: OwnerPicker
          ui:options:
            allowedKinds:
              - Group

2. Steps: The Scaffolder Action Pipeline

The spec.steps block executes a chain of modular, battle-tested Scaffolder actions.

Here is what a robust production pipeline does:

  steps:
    - id: template
      name: Fetch and Templatize Skeleton
      action: fetch:template
      input:
        url: ./skeleton
        values:
          component_id: ${{ parameters.component_id }}
          description: ${{ parameters.description }}
          owner: ${{ parameters.owner }}

    - id: publish
      name: Publish Repository to GitHub
      action: publish:github
      input:
        allowedHosts: ['github.com']
        description: ${{ parameters.description }}
        repoUrl: github.com?owner=acme-corp&repo=${{ parameters.component_id }}
        defaultBranch: main
        repoVisibility: private

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

  output:
    links:
      - title: Open GitHub Repository
        url: ${{ steps['publish'].output.remoteUrl }}
      - title: View Service in Backstage Catalog
        icon: catalog
        entityRef: ${{ steps['register'].output.entityRef }}

The Anchor: catalog-info.yaml in Git

The single most critical file generated by the template is catalog-info.yaml, which lives at the root of the newly created repository:

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: order-processing-service
  description: Handles customer checkout validation and billing settlement
  tags:
    - python
    - payments
  annotations:
    github.com/project-slug: acme-corp/order-processing-service
    prometheus.io/rule: order_service_latency
    pagerduty.com/integration-key: pd-service-orders
spec:
  type: service
  lifecycle: production
  owner: group:checkout-team
  system: payment-infrastructure

Why Storing catalog-info.yaml in Git Matters

  • Ownership as Code: Any change to service ownership or runbook URLs goes through a standard Git pull request.
  • Self-Healing Catalog: If an engineer deletes a repository or archives a project, Backstage detects the missing file and drops the component from the active catalog.
  • Day-One Incident Routing: When an alert fires in PagerDuty or Grafana, the link back to Backstage immediately reveals the owning team, current on-call engineer, API documentation, and Git commit history.

What Every Scaffolded Service Gets Out of the Box

A Golden Path template is only as good as the baseline standards baked into its skeleton. When our developers scaffold a service, they never write boilerplate infrastructure:

  1. Multi-Stage Dockerfile: An optimized container build using non-root runtime users and small base images (as explored in Why your Docker image is 1 GB).
  2. Standard CI/CD Approval Pipelines: A workflow that builds once, runs security scans, and promotes the exact same container digest across Dev, UAT, and Production approval gates.
  3. Telemetry & Health Checks: Pre-wired endpoints:
    • /healthz for liveness and readiness probes in Kubernetes.
    • /metrics for Prometheus scraping.
  4. Kubernetes Baseline Manifests: Pre-configured deployment YAML with sensible CPU/memory requests and limits, PodDisruptionBudgets, and ingress rules.

Common Pitfalls to Avoid

  • Creating monolithic "do-everything" templates: Don't attempt to build a single mega-template with dozens of checkboxes. Instead, create small, focused, runtime-specific templates (Go Microservice, Python Worker, Next.js Web App).
  • Hiding the pipeline logs: When a developer clicks "Create", stream the scaffolder execution logs live in the UI. If a step fails (e.g., GitHub organization permissions), the developer should immediately see why rather than staring at an infinite spinning wheel.
  • Forgetting template maintenance: Templates themselves are software. Put your template skeletons in automated testing pipelines that periodically scaffold an instance, run tests, and verify that dependencies haven't broken.

The Verdict

Backstage Software Templates transform platform engineering from a ticket-based bottleneck into a frictionless self-service engine.

The goal isn't just to save twenty minutes of setup time when creating a repo. The real win is that six months down the line, every microservice in your organization shares the same logging structure, the same deployment cadence, the same security posture, and a clearly identifiable owner.