Build Daily

Tinley Park · October 29, 2026
platformSpotify (creator); CNCF Backstage maintainersapplied

Backstage

Backstage is an open-source framework for building an internal developer portal: a software catalog fed from YAML files in Git, TechDocs (docs-as-code rendered with MkDocs), Software Templates and a plugin system. Each install is its own Node.js/React app, scaffolded with @backstage/create-app and backed by PostgreSQL in production. Apache 2.0, free, a CNCF incubating project with monthly releases. It fits once there are enough services that nobody can name them all and their owners from memory.

Updated October 17, 2026

Backstage is an open-source framework for building an internal developer portal. It keeps one catalog of every service, API, website and resource in an organization, with its owner, its dependencies and its documentation on the same page, so that information does not live in a wiki that goes out of date. The official docs at backstage.io/docs own the plugin and configuration reference.

What it is

Spotify built Backstage internally and released it in 2020. It joined the CNCF as a sandbox project on 2020-09-08 and moved to incubating on 2022-03-15. The licence is Apache 2.0.

Backstage is a framework, not a packaged binary. Each organization creates its own app with npx @backstage/create-app@latest, which gives a Yarn monorepo with a React frontend (packages/app) and a Node.js backend (packages/backend). Features are plugins (npm packages) added to that app and wired up in code or config. Upgrades are a code change: bump the Backstage packages, rebuild the image, deploy.

Releases follow a fixed schedule. The main line ships monthly, on the Tuesday before the third Wednesday of each month; a next pre-release line ships weekly. Patch releases ship only for critical bugs. High and critical security fixes are backported to the releases of the last six months where that is feasible. The current line is v1.55 (v1.55.3 shipped 2026-09-29), with v1.56 in next pre-releases. Backstage supports exactly two adjacent even-numbered Node.js releases; currently Node 22 and 24.

The three core features:

  • Software Catalog. Entities (Component, API, System, Domain, Resource, Group, User and others) described in catalog-info.yaml files, usually at the root of each repository.
  • TechDocs. Markdown docs that live next to the code, built into a static site with MkDocs and shown on the entity's page.
  • Software Templates (Scaffolder). Forms that create a new repository from a template, run actions and register the result in the catalog.

Search, Kubernetes, permissions and hundreds of community plugins sit on top of these.

How it works

One backend process serves the frontend bundle and every backend plugin on port 7007. The catalog backend stores entities in PostgreSQL. Users sign in through an auth provider, and a sign-in resolver maps the external identity to a User entity in the catalog. The figure shows the three main flows: sign-in, catalog ingestion, and a TechDocs page view with the default local builder.

How Backstage signs a user in, ingests the catalog and serves TechDocs

Step 1 of 5. The browser opens Backstage and picks the OIDC sign-in. The auth backend starts the authorization code flow.

One backend on port 7007 carries all three flows. TechDocs is shown with the local builder; the recommended setup builds docs in CI instead.

The life of a catalog entity

The catalog runs in three stages (life of an entity):

  1. Ingestion. Entity providers fetch raw data and write unprocessed entities. Each provider has a stable ID, and two providers may not emit the same entity. Only coarse checks run here (kind and metadata.name must exist).
  2. Processing. Processors run in registration order (the catalog.processorOptions.<name>.priority setting changes the order; default 20, lower runs first). They can change the entity and emit new entities, relations and errors. An entity with errors does not replace its last good version.
  3. Stitching. The stitcher merges the processed entity, its errors and all relations into the final entity that the API serves. It runs only when the entity's hash changes.

When a parent stops emitting an entity and nothing else references it, the entity becomes an orphan and gets backstage.io/orphan: 'true'. The default orphanStrategy: delete removes orphans; keep holds them until someone deletes them.

TechDocs: basic and recommended setup

Aspect Basic (builder: local) Recommended (builder: external)
Who builds the docs The TechDocs backend, on first view A CI job in each repository, with techdocs-cli
Where the site is stored Local disk, or a cloud publisher Cloud storage (S3, GCS, Azure Blob Storage)
How it is served Backend returns the files from storage Backend fetches the files from storage and returns them
Credentials Backend needs read and write CI needs write; the backend needs read only

When to use it

Reach for it when:

  • There are enough services, jobs and sites that "who owns this?" and "where are the docs?" no longer have an answer in anyone's head.
  • Documentation should live next to the code and be reviewed in the same pull request as the code.
  • New services should start from a standard template that also registers them.
  • One sign-in and one search across the catalog, docs and plugins (Kubernetes, CI, alerts) is worth a small TypeScript codebase.

Skip it when:

  • There are a handful of repositories and a README per repository answers the questions.
  • Nobody can own a Node.js/React app. Backstage needs upgrades every month or two, and skipped releases pile up breaking changes.
  • The goal is a status page or a dashboard of metrics. Grafana does that with less code.

At a glance

Core concepts

  • Entity. A YAML document with apiVersion, kind, metadata and spec. References use the form kind:namespace/name, for example component:default/web.
  • System model. Components and APIs belong to Systems; Systems belong to Domains; Groups and Users own all of them.
  • Location. A pointer to one or more catalog-info.yaml files, registered in the UI or listed under catalog.locations.
  • Annotations. Keys that plugins read: backstage.io/techdocs-ref: dir:. for docs, backstage.io/source-location, github.com/project-slug: <owner>/<repo>.
  • Sign-in resolver. The function that turns an external identity into a User entity reference.
  • Backend and frontend systems. The plugin wiring APIs. New frontend plugins are published under an /alpha export while the new frontend system matures.

Key facts

Item Value
Licence Apache 2.0
Current line v1.55 (v1.55.3, 2026-09-29)
Release cadence Monthly main line, weekly next
Node.js 22 and 24
Package manager Yarn 4 (create-app pins 4.4.1)
Production database PostgreSQL
Backend port 7007
Helm chart backstage/charts, chart v2.10.x
Dev machine At least 6 GB memory and 20 GB disk

How to integrate

Create the app and build an image

npx @backstage/create-app@latest   # prompts for a name
cd my-backstage-app
yarn install
yarn tsc && yarn build:backend     # host build: frontend bundled into the backend
yarn build-image --tag registry.example.com/backstage:1.55.3

yarn build-image uses the Dockerfile in packages/backend. The image runs node packages/backend and reads app-config.yaml plus any extra config files passed with --config.

Production config: PostgreSQL, OIDC, TechDocs

# app-config.production.yaml
app:
  baseUrl: https://backstage.example.com
backend:
  baseUrl: https://backstage.example.com
  listen:
    port: 7007
  database:
    client: pg
    connection:
      host: ${POSTGRES_HOST}
      port: ${POSTGRES_PORT}
      user: ${POSTGRES_USER}
      password: ${POSTGRES_PASSWORD}

auth:
  environment: production
  session:
    secret: ${AUTH_SESSION_SECRET}
  providers:
    oidc:
      production:
        metadataUrl: https://sso.example.com/realms/main/.well-known/openid-configuration
        clientId: ${AUTH_OIDC_CLIENT_ID}
        clientSecret: ${AUTH_OIDC_CLIENT_SECRET}
        prompt: auto
        signIn:
          resolvers:
            - resolver: emailMatchingUserEntityProfileEmail

techdocs:
  builder: local          # or 'external' when CI publishes the docs
  generator:
    runIn: local          # 'docker' needs a Docker socket; not available in a normal pod
  publisher:
    type: local           # or awsS3 | googleGcs | azureBlobStorage

catalog:
  locations:
    - type: url
      target: https://github.com/example/web/blob/main/catalog-info.yaml

The OIDC provider needs the backend module @backstage/plugin-auth-backend-module-oidc-provider, added to packages/backend/src/index.ts. The frontend needs its own API ref for the provider; the OIDC docs show the example, which uses Keycloak. The provider.id passed to OAuth2.create must stay oidc.

A catalog entry with TechDocs

# catalog-info.yaml at the repository root
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: web
  description: Public website
  annotations:
    backstage.io/techdocs-ref: dir:.
spec:
  type: website
  lifecycle: production
  owner: group:default/platform
  system: storefront

Put an mkdocs.yml next to it and the Markdown pages under docs/.

Deploy with Helm

helm repo add backstage https://backstage.github.io/charts
helm upgrade --install backstage backstage/backstage -n backstage --create-namespace -f values.yaml
# values.yaml
backstage:
  image:
    registry: registry.example.com
    repository: backstage
    tag: "1.55.3"
  extraEnvVarsSecrets:
    - backstage-secrets        # POSTGRES_*, AUTH_* from a Secret, never from values
  extraAppConfig:
    - filename: app-config.production.yaml
      configMapRef: backstage-app-config
postgresql:
  enabled: false               # point at an existing PostgreSQL instead

The chart's default image is the upstream demo app (ghcr.io/backstage/backstage:latest); a real install sets its own image.

In the GL stack

Backstage 1.55.0 runs on the management cluster, one of three K3s clusters behind builddaily.io, paiddaily.io and the other products.

  • What it holds. The software catalog lists every service in the stack, and each service's TechDocs render on its catalog page. That gives one place to look up a service, its owner and its documentation across all three clusters.
  • Sign-in. Users sign in through Keycloak.
  • Version. The install is on 1.55.0; the 1.55 line has since had patch releases up to 1.55.3.

Gotchas

  • Sign-in needs a User entity. A resolver that finds no matching User fails with "unable to resolve user identity." Import Users and Groups from the identity source, or define them in YAML. The dangerouslyAllowSignInWithoutUserInCatalog resolver option bypasses the check, and the docs warn against it in production.
  • Auth config does not hot-reload. Changes to auth providers need a restart of the backend.
  • v1.55 breaking changes. The catalog dropped catalog.enableRelationsCompatibility (relations now carry only targetRef), the CodeOwnersProcessor and AnnotateScmSlugEntityProcessor (now community modules) and the catalog.stitchingStrategy.mode key. The Elasticsearch search module needs Elasticsearch 8.19 or later. The Cloudflare Access auth provider now requires auth.providers.cfaccess.audience. Read the release notes for each minor before upgrading.
  • TechDocs MkDocs plugin allowlist (v1.55). Generation now strips MkDocs plugins that are not on the built-in allowlist and logs a warning. Add needed plugins to techdocs.generator.mkdocs.dangerouslyAllowAdditionalPlugins.
  • runIn: docker in Kubernetes. The default generator runs MkDocs in a container, which a normal pod cannot start. Set runIn: local and install mkdocs-techdocs-core in the image, or build docs in CI with the external builder.
  • SQLite is for development. The scaffolded app uses an in-memory SQLite database; production needs PostgreSQL.
  • Permission policy and new permissions. v1.55 applies templateDryRunPermission to inline template dry runs. A policy that denies unknown permissions must allow scaffolder.template.dry-run to keep dry runs working.

Risks

  • Upgrade cost. Each install is its own codebase. Monthly releases with breaking changes mean an upgrade is work; falling several minors behind makes it more work. backstage-cli versions:bump helps but does not remove the migration steps.
  • Plugin quality varies. Core plugins are maintained by the project; community plugins live in a separate repository with their own maintainers and pace.
  • Credential concentration. The backend holds tokens for Git, CI and clusters so that plugins can read them. Scope those tokens to read-only where possible, and turn on the permission framework (permission.enabled: true) when not every user should see everything.
  • Project health. Healthy: CNCF incubating, many maintainers, a fixed release schedule.

Alternatives

  • Red Hat Developer Hub. A supported Backstage distribution with dynamic plugins, for teams that want Backstage without maintaining their own app.
  • Roadie. Hosted Backstage, for teams that want the catalog and TechDocs without running the app.
  • Port. A commercial portal with a configurable data model and no code to own, for teams that do not want a TypeScript codebase.
  • A docs site plus a service list. MkDocs or Docusaurus with a hand-kept table of services, for a small number of repositories where a full portal is more than the problem needs.

Related