Platform Engineering with Backstage 2026: Build an Internal Developer Platform from Scratch

Platform Engineering with Backstage 2026: Build an Internal Developer Platform from Scratch

Platform Engineering vs DevOps

DevOps solved a real problem: it tore down the wall between development and operations. But as organizations scaled to hundreds of engineers and dozens of microservices, a new problem emerged — cognitive overload. Every team reinvented the same wheels: CI pipelines, Kubernetes manifests, observability stacks, secret management patterns, and on-call runbooks. Developers spent more time navigating infrastructure than shipping features.

Platform Engineering is the response. Rather than asking every team to become infrastructure experts, it establishes golden paths — opinionated, well-maintained routes that let product engineers focus on business logic. The platform team builds and operates the Internal Developer Platform (IDP); product teams consume it through self-service interfaces.

The difference in day-to-day experience is stark. In a pure DevOps model, spinning up a new service means reading three wikis, copying a colleague's pipeline YAML, asking in Slack who owns the staging cluster, and hoping the Terraform module still works. On a mature IDP, you fill out a form, click submit, and receive a new GitHub repository pre-wired with CI/CD, monitoring, and Kubernetes deployment in under two minutes.

Backstage is the open-source framework that most platform teams reach for when building that self-service portal.


What Backstage Is

Backstage was created by Spotify to manage thousands of internal services. Spotify open-sourced it in 2020 and donated it to the Cloud Native Computing Foundation (CNCF), where it graduated as an incubating project. Today it is the de-facto standard for building developer portals.

At its core, Backstage is a React/Node.js web application built around three primitives:

  • Software Catalog — a live inventory of every service, library, website, API, and infrastructure component in your organization.
  • Software Templates (Scaffolder) — form-driven workflows that create new components and wire up boilerplate automatically.
  • TechDocs — a "docs as code" system that renders Markdown documentation from each repo directly inside the portal.

Everything else — Kubernetes dashboards, cost tracking, incident management, GitHub Actions views — is a plugin. The plugin architecture means you can add first-party or community plugins, or write your own, without forking the core. The plugin marketplace lists over 200 community contributions.


Prerequisites and Installation

You need Node.js 20+, Yarn, Docker (for local TechDocs rendering), and a GitHub account with a personal access token scoped to repo and read:org.

Create a new Backstage app:

npx @backstage/create-app@latest
# Prompted for app name — use: my-idp
cd my-idp
yarn dev

Backstage starts on http://localhost:3000. The default setup uses an in-memory SQLite database and a static catalog. Before you go further, switch to PostgreSQL by installing the backend plugin and updating app-config.yaml:

# app-config.yaml (excerpt)
backend:
  database:
    client: pg
    connection:
      host: ${POSTGRES_HOST}
      port: 5432
      user: ${POSTGRES_USER}
      password: ${POSTGRES_PASSWORD}
      database: backstage

For production, deploy the backend as a Docker container and the frontend as a static site behind a CDN. The official Backstage Helm chart (backstage/backstage on Artifact Hub) handles this with minimal configuration.


The Software Catalog

The catalog is the heart of every internal developer platform tutorial. It answers the question every engineer eventually asks: "What services exist, who owns them, and where is the runbook?"

catalog-info.yaml for a Microservice

Every component registers itself by committing a catalog-info.yaml file at the repository root:

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: payments-service
  description: Handles payment processing and refunds
  annotations:
    github.com/project-slug: acme-corp/payments-service
    backstage.io/techdocs-ref: dir:.
    kubernetes.io/label-selector: app=payments-service
  tags:
    - python
    - fastapi
    - payments
spec:
  type: service
  lifecycle: production
  owner: team-payments
  system: commerce
  dependsOn:
    - component:orders-service
    - resource:postgres-payments
  providesApis:
    - payments-api

The dependsOn and providesApis fields make the catalog a live dependency graph. Backstage renders these as an interactive diagram, so an on-call engineer can immediately see upstream and downstream blast radius.

GitHub Org Discovery

Manually adding every repo's catalog-info.yaml to app-config.yaml does not scale. The GitHub discovery provider scans your entire organization automatically:

# app-config.yaml
catalog:
  providers:
    github:
      acme-org:
        organization: acme-corp
        catalogPath: /catalog-info.yaml
        filters:
          branch: main
          repository: '.*'
        schedule:
          frequency: { minutes: 30 }
          timeout: { minutes: 3 }

Install the provider package:

yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-github

Register it in packages/backend/src/index.ts:

backend.add(import('@backstage/plugin-catalog-backend-module-github'));

Backstage will now index every repository that contains a catalog-info.yaml on a 30-minute cycle. New services appear automatically once the file is merged.


Software Templates (Scaffolder)

The Scaffolder is the self-service engine. It lets developers fill in a form — service name, team owner, description — and receive a fully bootstrapped repository with all your organization's standards baked in.

Creating a Python FastAPI Template

Templates live in your catalog. Create a templates/fastapi-service/template.yaml:

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: python-fastapi-service
  title: Python FastAPI Microservice
  description: Creates a new FastAPI service with CI/CD, Dockerfile, and Kubernetes manifests
  tags:
    - python
    - fastapi
    - recommended
spec:
  owner: team-platform
  type: service

  parameters:
    - title: Service details
      required: [name, description, owner]
      properties:
        name:
          title: Service name
          type: string
          pattern: '^[a-z][a-z0-9-]*$'
          description: Lowercase, hyphens only (e.g. invoice-processor)
        description:
          title: Short description
          type: string
        owner:
          title: Owning team
          type: string
          ui:field: OwnerPicker
          ui:options:
            catalogFilter:
              kind: Group

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

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

    - id: register
      name: Register in catalog
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
        catalogInfoPath: /catalog-info.yaml

  output:
    links:
      - title: Repository
        url: ${{ steps.publish.output.remoteUrl }}
      - title: Open in catalog
        url: ${{ steps.register.output.catalogEntityUrl }}

The skeleton/ directory next to template.yaml contains the actual file templates with ${{ values.name }} placeholders. Include a Dockerfile, GitHub Actions workflow, catalog-info.yaml, mkdocs.yml, and a bare-bones FastAPI main.py. When a developer submits the form, Backstage materializes those templates, creates the GitHub repository, pushes the code, and registers the new service in the catalog — all in one atomic workflow.

This is developer self-service at its best: a product engineer has a production-ready, standards-compliant service skeleton in under two minutes without filing a ticket or pinging the platform team on Slack.


Kubernetes Plugin

The Kubernetes plugin surfaces pod health, resource consumption, and recent rollout status directly on each service's catalog page. Engineers never need to leave the portal to check whether their latest deployment is healthy.

Install the frontend and backend plugins:

yarn --cwd packages/app add @backstage/plugin-kubernetes
yarn --cwd packages/backend add @backstage/plugin-kubernetes-backend

Configure cluster access in app-config.yaml:

kubernetes:
  serviceLocatorMethod:
    type: multiTenant
  clusterLocatorMethods:
    - type: config
      clusters:
        - name: production-us-east
          url: ${K8S_CLUSTER_URL}
          authProvider: serviceAccount
          serviceAccountToken: ${K8S_SERVICE_ACCOUNT_TOKEN}
          caData: ${K8S_CA_DATA}

The plugin links a catalog component to its Kubernetes workloads via the annotation in catalog-info.yaml:

annotations:
  kubernetes.io/label-selector: app=payments-service,env=production

Backstage then displays live pod counts, CPU and memory graphs, and the status of any active rollout — giving every on-call engineer immediate situational awareness without kubectl access.


TechDocs: Auto-Generated Documentation

TechDocs converts the docs/ directory in each repository into a searchable, versioned documentation site rendered inside Backstage. There is no separate doc hosting to manage.

Add mkdocs.yml to your service skeleton:

site_name: Payments Service
docs_dir: docs
plugins:
  - techdocs-core
nav:
  - Home: index.md
  - Architecture: architecture.md
  - Runbook: runbook.md

The annotation in catalog-info.yaml (backstage.io/techdocs-ref: dir:.) tells Backstage where to find the docs. In CI, build and publish the static site to object storage (S3, GCS) using the @techdocs/cli tool. In production mode, Backstage reads from the object store and skips on-the-fly generation, which is much faster for large organizations.


GitHub Actions Plugin

The GitHub Actions plugin embeds pipeline run history directly on a service's catalog page. Developers see their latest build status, test results, and deployment pipeline without opening a second browser tab.

yarn --cwd packages/app add @backstage-community/plugin-github-actions

Add the required annotation to catalog-info.yaml:

annotations:
  github.com/project-slug: acme-corp/payments-service

The plugin picks up the GITHUB_TOKEN from app-config.yaml and renders a paginated table of workflow runs with status badges, timestamps, and direct links to individual job logs.


Measuring IDP Success

Shipping an IDP is an investment. Justify it with data. The four DORA metrics map directly to the value a mature platform delivers:

MetricWhat to measureTarget (Elite)
Deployment frequencyDeployments per team per dayOn demand (multiple/day)
Lead time for changesCommit to productionLess than one hour
Change failure rate% of deployments causing incidents0 – 15%
Time to restore serviceMean time to recoveryLess than one hour

Track these in your observability platform and surface them in Backstage via a custom plugin or the community @backstage-community/plugin-dora plugin. After six months of IDP adoption, the clearest signal is a drop in "how do I deploy?" Slack messages and a rise in self-service template usage — both easy to measure.


FAQ

Do I need Kubernetes to run Backstage? No. You can run Backstage as a Node.js process on any VM or container platform. Kubernetes is only required if you want to display workload health with the Kubernetes plugin.

Can I use Backstage without GitHub? Yes. There are catalog discovery providers for GitLab, Bitbucket, Azure DevOps, and Gitea. The Scaffolder has publish actions for all major Git hosts.

How long does an initial Backstage rollout take? A minimal portal with a catalog and one template typically takes two to four weeks for a two-person platform team. Full plugin integration (Kubernetes, TechDocs, GitHub Actions) adds another two to three weeks.

Is Backstage suitable for small teams? Backstage pays off at around 20+ engineers or 30+ microservices. Below that, the maintenance overhead may outweigh the self-service benefits. Consider the simpler port.io or OpsLevel SaaS alternatives for smaller organizations.

How do I keep the catalog accurate over time? Enable auto-discovery so that new repos register themselves when they add a catalog-info.yaml. Automate the file's creation via your service template. Add a linting step in CI that validates the schema. Mark components as lifecycle: deprecated when services are decommissioned rather than deleting the entity — historical context is valuable.

Leonardo Lazzaro

Software engineer and technical writer. 10+ years experience in DevOps, Python, and Linux systems.

More articles by Leonardo Lazzaro