platformengineering/backstage cncf

Tldr

Backstage is an open-source developer portal platform built by Spotify and donated to the Cloud Native Computing Foundation (CNCF). It centralizes tooling, services, documentation, and infrastructure into a single, unified developer experience.
It acts as an interactive front-end on top of complex backend tools, allowing teams to monitor and control infrastructure, application catalogs, and CI/CD workflows without relying strictly on terminal commands

1. What Is Backstage?

1Backstage is a developer portal framework that solves the β€œtooling sprawl” problem in large engineering organizations. Instead of engineers jumping between dozens of dashboards, CI/CD tools, cloud consoles, and wikis, Backstage brings everything into one place.

  • Created by: Spotify (2020, open-sourced)
  • Adopted by: CNCF (Incubating β†’ Graduated project)
  • Language: TypeScript / React (frontend), Node.js (backend)
  • License: Apache 2.0

Key Concepts

  1. Source Code, Not a Product: Unlike typical software, Backstage provides no official out-of-the-box Docker images or Helm charts to deploy directly. It is a codebase you download, modify locally, and customize to your business needs.
  2. Plugins: Its functionality is highly extensible through a modular ecosystem. You can install pre-built community plugins or write your own.
  3. Components: These represent logical entities in your systemβ€”such as front-end websites, backend APIs, databases, or managed Kubernetes clusters.
  4. Templates: These define customized, dynamic UI wizard forms that let users generate infrastructure configurations easily without having to write code from scratch

Managing Backstage requires significant TypeScript and Node.js expertise, and configuring everything can feel exceptionally verbose and redundant compared to standard Kubernetes setups.

Demo

2. Core Problem Backstage Solves

ProblemBackstage Solution
Engineers can’t find services or their ownersSoftware Catalog
Documentation is scattered everywhereTechDocs
Creating new services is slow & inconsistentSoftware Templates (Scaffolder)
Too many dashboards to checkUnified Plugin Interface
Infrastructure visibility is siloedKubernetes, CI/CD Plugins

3. Architecture Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      Backstage App                      β”‚
β”‚                                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚   Frontend   β”‚  β”‚    Plugins     β”‚  β”‚  Backend   β”‚   β”‚
β”‚  β”‚  (React SPA) β”‚  β”‚  (UI + APIs)   β”‚  β”‚ (Node.js)  β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚         β”‚                  β”‚                  β”‚         β”‚
β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”‚                            β”‚                            β”‚
β”‚              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”             β”‚
β”‚              β”‚      Backstage Core APIs   β”‚             β”‚
β”‚              β”‚  Catalog / Auth / Search / β”‚             β”‚
β”‚              β”‚  Config / Identity / Proxy β”‚             β”‚
β”‚              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Backstage has three tiers:

  1. Core β€” Foundational APIs (Catalog API, Config API, Auth API, etc.)
  2. App β€” Your organization’s customized Backstage instance
  3. Plugins β€” Feature modules (first-party + third-party + custom)

4. The Software Catalog

The Software Catalog is the heart of Backstage. It is a registry of all your organization’s software and infrastructure.

Entity Kinds

KindDescriptionExample
ComponentA deployable unit of softwareMicroservice, website, library
APIAn interface exposed by a componentREST, gRPC, GraphQL, AsyncAPI
ResourceInfrastructure a component depends onS3 bucket, PostgreSQL DB
SystemA collection of related components/APIsPayment System
DomainA business domain grouping systemsCheckout Domain
GroupAn organizational team or business unitBackend Team
UserAn individual developerjohn.doe
LocationA pointer to other catalog YAML filesGitHub org file
TemplateA Software Template for scaffoldingNode.js Service Template

The catalog-info.yaml File

Every entity is described by a YAML descriptor file committed to the code repository:

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: payment-service
  description: Handles all payment transactions
  tags:
    - payments
    - node
  annotations:
    github.com/project-slug: myorg/payment-service
    backstage.io/techdocs-ref: dir:.
spec:
  type: service
  lifecycle: production
  owner: payments-team
  system: checkout-system
  providesApis:
    - payment-api
  dependsOn:
    - resource:postgres-payments-db

Key Metadata Fields

  • apiVersion β€” always backstage.io/v1alpha1 or v1beta1
  • kind β€” entity type (Component, API, System, etc.)
  • metadata.name β€” unique identifier
  • metadata.annotations β€” integration hooks (GitHub, PagerDuty, Sentry, etc.)
  • spec.owner β€” team or user responsible
  • spec.lifecycle β€” experimental, production, deprecated
  • spec.type β€” component type: service, website, library, tool

How Catalog Ingestion Works

Repository (catalog-info.yaml)
        β”‚
        β–Ό
  Catalog Processor
        β”‚
  β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
  β”‚  Validate  β”‚
  β”‚  Resolve   β”‚
  β”‚  Relations β”‚
  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
        β”‚
  β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚   Catalog Database     β”‚
  β”‚ (SQLite / PostgreSQL)  β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β”‚
  Backstage UI + API

5. Software Templates (Scaffolder)

The Scaffolder lets teams create new services, libraries, or resources using pre-defined templates β€” with a form-driven wizard UI.

Template Structure

apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: nodejs-service-template
  title: Node.js Microservice
  description: Creates a production-ready Node.js service
  tags:
    - node
    - recommended
spec:
  owner: platform-team
  type: service
 
  parameters:
    - title: Service Details
      required: [name, description]
      properties:
        name:
          title: Service Name
          type: string
          pattern: '^[a-z0-9-]+$'
        description:
          title: Description
          type: string
        owner:
          title: Owner
          type: string
          ui:field: OwnerPicker
 
  steps:
    - id: fetch
      name: Fetch Template
      action: fetch:template
      input:
        url: ./skeleton
        values:
          name: ${{ parameters.name }}
          description: ${{ parameters.description }}
 
    - id: publish
      name: Publish to GitHub
      action: publish:github
      input:
        allowedHosts: ['github.com']
        repoUrl: github.com?owner=myorg&repo=${{ parameters.name }}
 
    - 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
        entityRef: ${{ steps.register.output.entityRef }}

Built-in Scaffolder Actions

ActionDescription
fetch:templateRender a Cookiecutter/Nunjucks template
fetch:plainCopy files as-is
publish:githubCreate a GitHub repository
publish:gitlabCreate a GitLab project
publish:bitbucketCreate a Bitbucket repo
catalog:registerRegister new entity in catalog
catalog:writeWrite catalog YAML to workspace
github:actions:dispatchTrigger a GitHub Actions workflow

6. TechDocs

TechDocs is Backstage’s docs-as-code solution. It renders Markdown documentation (written with MkDocs) directly inside Backstage.

How It Works

/docs/
  index.md
  architecture.md
  api-reference.md
mkdocs.yml
catalog-info.yaml  ← links to docs

mkdocs.yml Example

site_name: Payment Service
docs_dir: docs
nav:
  - Home: index.md
  - Architecture: architecture.md
  - API Reference: api-reference.md
plugins:
  - techdocs-core

Annotation in catalog-info.yaml

annotations:
  backstage.io/techdocs-ref: dir:.

TechDocs Build Modes

ModeDescription
localBuild docs on-the-fly (dev/testing)
externalPre-built docs stored in object storage (S3, GCS, Azure Blob) β€” recommended for production

7. Plugins

Plugins are the extension mechanism of Backstage. Everything in Backstage is a plugin β€” even the Catalog and Scaffolder.

Plugin Types

TypeDescription
Frontend PluginReact-based UI tab/page added to Backstage
Backend PluginExpress.js route/service on the Backstage backend
Backend ModuleExtension point for existing backend plugins
PluginFunction
@backstage/plugin-catalogSoftware Catalog UI
@backstage/plugin-scaffolderTemplate Wizard
@backstage/plugin-techdocsDocs viewer
@backstage/plugin-searchGlobal search
@backstage/plugin-kubernetesK8s workload viewer
@backstage/plugin-cost-insightsCloud cost visibility
@backstage/plugin-github-actionsCI/CD pipeline view
@backstage/plugin-pagerdutyOn-call / incident data

Creating a Custom Plugin

# Scaffold a new plugin
yarn backstage-cli new --select plugin

This generates:

plugins/
  my-plugin/
    src/
      components/
        ExampleComponent/
      index.ts
      plugin.ts
      routes.ts
    package.json

Registering a Plugin in the App

// packages/app/src/App.tsx
import { MyPluginPage } from '@internal/plugin-my-plugin';
 
const routes = (
  <FlatRoutes>
    <Route path="/my-plugin" element={<MyPluginPage />} />
  </FlatRoutes>
);

8. Authentication & Identity

Backstage supports multiple auth providers out of the box.

Supported Providers

  • GitHub, GitLab, Google, Microsoft Azure AD
  • Okta, Auth0, OneLogin, Bitbucket
  • Guest (development only)
  • Custom OIDC/SAML

Configuration (app-config.yaml)

auth:
  environment: production
  providers:
    github:
      production:
        clientId: ${AUTH_GITHUB_CLIENT_ID}
        clientSecret: ${AUTH_GITHUB_CLIENT_SECRET}

Sign-In Resolvers

Backstage maps external identity (GitHub username) to an internal catalog User entity:

providers.github.create({
  signIn: {
    resolver: providers.github.resolvers.usernameMatchingUserEntityName(),
  },
})

Backstage has a pluggable search framework with full-text search across catalog entities, TechDocs, and custom sources.

Search Engines

EngineUse Case
lunrIn-memory, no infra, dev/small deployments
ElasticsearchProduction, scalable
OpenSearchProduction, AWS-compatible
PostgresSimpler production option

Indexing Flow

Collators (gather data) β†’ Decorators (enrich) β†’ Index (search engine) β†’ Query API β†’ UI

10. Kubernetes Plugin

The Kubernetes plugin surfaces workload status for any component directly in the catalog.

Annotation Required

annotations:
  backstage.io/kubernetes-id: payment-service
  backstage.io/kubernetes-namespace: production

Features

  • View Deployments, Pods, ReplicaSets, StatefulSets
  • See pod logs and errors
  • Multi-cluster support
  • Rollout status

11. Configuration (app-config.yaml)

The central configuration file for Backstage:

app:
  title: My Company Developer Portal
  baseUrl: https://backstage.mycompany.com
 
organization:
  name: My Company
 
backend:
  baseUrl: https://backstage.mycompany.com
  listen:
    port: 7007
  database:
    client: pg
    connection:
      host: ${POSTGRES_HOST}
      port: ${POSTGRES_PORT}
      user: ${POSTGRES_USER}
      password: ${POSTGRES_PASSWORD}
 
catalog:
  locations:
    - type: url
      target: https://github.com/myorg/catalog/blob/main/all.yaml
    - type: github-discovery
      target: https://github.com/myorg
 
techdocs:
  builder: external
  generator:
    runIn: docker
  publisher:
    type: awsS3
    awsS3:
      bucketName: my-techdocs-bucket
      region: us-east-1
 
integrations:
  github:
    - host: github.com
      token: ${GITHUB_TOKEN}

12. Deployment

                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     Users        β”‚   Ingress /  β”‚
    ──────────►   β”‚   Load Bal.  β”‚
                  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚   Backstage Pod(s)  β”‚
              β”‚  (Frontend + Backendβ”‚
              β”‚   in one container) β”‚
              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
          β”‚              β”‚              β”‚
    β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”
    β”‚ PostgreSQLβ”‚ β”‚  Object     β”‚ β”‚  Redis   β”‚
    β”‚ (Catalog  β”‚ β”‚  Storage    β”‚ β”‚  (Cache) β”‚
    β”‚  + Auth)  β”‚ β”‚ (TechDocs)  β”‚ β”‚          β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Docker / Kubernetes

# Build image
FROM node:18-bookworm-slim
WORKDIR /app
COPY . .
RUN yarn install --frozen-lockfile
RUN yarn tsc
RUN yarn build:backend
CMD ["node", "packages/backend", "--config", "app-config.yaml"]

13. Entity Relationships

Backstage uses declarative relationships between entities:

Domain ──contains──► System
System ──contains──► Component
Component ──provides──► API
Component ──consumes──► API
Component ──dependsOn──► Resource
Component ──ownedBy──► Group
Group ──memberOf──► Group (parent)

In YAML:

spec:
  system: checkout-system          # Component belongs to a System
  owner: payments-team             # Owned by a Group
  providesApis: [payment-api]      # Exposes an API
  consumesApis: [fraud-api]        # Consumes another API
  dependsOn:
    - resource:postgres-payments   # Depends on a Resource
    - component:notification-svc   # Depends on another Component

14. Annotations Reference

AnnotationPurpose
github.com/project-slugLinks entity to GitHub repo
backstage.io/techdocs-refTechDocs source location
backstage.io/kubernetes-idK8s workload label
pagerduty.com/service-idPagerDuty service
sentry.io/project-slugSentry project
sonarqube.org/project-keySonarQube analysis
jenkins.io/job-full-nameJenkins job
lighthouse.com/website-urlLighthouse audit URL
jira/project-keyJira project board
opsgenie.com/component-selectorOpsGenie alerts
datadoghq.com/dashboard-urlDatadog dashboard

15. Key CLI Commands

# Create a new Backstage app
npx @backstage/create-app@latest
# Start development server
yarn dev
# Add a new plugin
yarn backstage-cli new --select plugin
# Add a new backend plugin
yarn backstage-cli new --select backend-plugin
# Type check
yarn tsc
# Build
yarn build:backend
# Run tests
yarn test
# Upgrade Backstage packages
yarn backstage-cli versions:bump

16. Glossary

TermDefinition
EntityAny item registered in the Software Catalog
DescriptorThe catalog-info.yaml file defining an entity
ScaffolderTemplate-based project creation system
TechDocsDocs-as-code documentation system
PluginA modular feature extension for Backstage
ProcessorComponent that processes/validates catalog entities
ProviderComponent that discovers and ingests catalog entities
CollatorSearch indexing component
DecoratorEnriches search documents before indexing
LocationA pointer to one or more catalog descriptor files
LifecycleStage of a component: experimental, production, deprecated
OwnerGroup or User responsible for an entity

17. Summary

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Backstage at a Glance              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Software Catalog β”‚ Discover & own all services  β”‚
β”‚ Scaffolder       β”‚ Create new services fast     β”‚
β”‚ TechDocs         β”‚ Docs live next to code       β”‚
β”‚ Search           β”‚ Find anything, anywhere      β”‚
β”‚ Kubernetes       β”‚ See workload health in UI    β”‚
β”‚ Plugins          β”‚ Extend with 100s of plugins  β”‚
β”‚ Auth             β”‚ GitHub, Google, Okta & more  β”‚
β”‚ Config           β”‚ One YAML to rule them all    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Backstage turns infrastructure chaos into a single pane of glass for every developer in your organization.

Footnotes

  1. backstage.io | CNCF Backstage Project | GitHub: backstage/backstage ↩