Backstage Internal Developer Portal: Complete Setup Guide 2026
Learn how to set up Backstage IDP from scratch. Covers software catalog, TechDocs, plugins, CI/CD integration, and building a self-service developer platform your team will actually use.
Backstage is Spotifyβs open-source developer portal, now a CNCF incubating project. It solves a real problem: as engineering organizations grow, developers spend more time finding things β documentation, services, runbooks, ownership β than building them.
A well-configured Backstage instance becomes the single pane of glass for your engineering org: every service catalogued, every doc discoverable, every workflow self-serviceable. This guide walks through setting it up properly.
What Backstage Actually Is
Backstage is a React + Node.js application you host yourself. At its core it provides three things:
- Software Catalog β a registry of every service, library, website, pipeline, and team in your org
- TechDocs β documentation as code, sourced from repos and rendered in-portal
- Software Templates β scaffolding for creating new services with pre-configured CI/CD, logging, and standards baked in
On top of that foundation, a plugin ecosystem (~200+ plugins) handles observability, incident management, cost allocation, API docs, and anything else your stack needs.
Prerequisites
- Node.js 18+
- Yarn 1.x or 3.x
- PostgreSQL (for production; SQLite works for development)
- A GitHub/GitLab/Bitbucket account (for catalog entity discovery)
Initial Setup
npx @backstage/create-app@latest
cd my-backstage-app
yarn dev
The CLI scaffolds a full Backstage app. Youβll see a working portal at http://localhost:3000 with example catalog entries.
Directory structure:
my-backstage-app/
βββ app-config.yaml # Main configuration
βββ app-config.production.yaml
βββ packages/
β βββ app/ # Frontend (React)
β βββ backend/ # Backend (Node.js)
βββ plugins/ # Custom plugins
The Software Catalog
The catalog is the heart of Backstage. Every entity is described by a YAML file (called a βcatalog-info.yamlβ) stored in the entityβs repository.
Entity Kinds
| Kind | What it represents |
|---|---|
Component | A service, library, website, or CLI tool |
API | An interface exposed by a component |
System | A collection of related components |
Domain | A business area containing systems |
Resource | Infrastructure (databases, S3 buckets, queues) |
Group | A team or organizational unit |
User | An individual developer |
A Typical catalog-info.yaml
# In your service's repository: ./catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: payment-service
description: Handles all payment processing and billing
annotations:
github.com/project-slug: myorg/payment-service
backstage.io/techdocs-ref: dir:.
pagerduty.com/service-id: P1234567
tags:
- payments
- critical
- nodejs
links:
- url: https://grafana.internal/d/payments
title: Grafana Dashboard
icon: dashboard
spec:
type: service
owner: group:payments-team
lifecycle: production
system: checkout-system
dependsOn:
- component:postgres-payments
- component:stripe-api
providesApis:
- payment-api
Configuring Catalog Discovery
Tell Backstage where to find catalog-info.yaml files in app-config.yaml:
catalog:
rules:
- allow: [Component, API, System, Domain, Resource, Group, User]
providers:
github:
default:
organization: myorg
catalogPath: /catalog-info.yaml
filters:
branch: main
repository: '.*' # All repos
schedule:
frequency: { minutes: 30 }
timeout: { minutes: 3 }
For GitLab or Bitbucket, install the corresponding discovery plugin:
yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-gitlab
TechDocs: Documentation as Code
TechDocs renders Markdown docs (written in MkDocs format) from your repos into Backstage. Engineers update docs in PRs alongside code β no separate wiki, no drift.
Setup in a Service Repo
- Add to
catalog-info.yaml:
metadata:
annotations:
backstage.io/techdocs-ref: dir:.
- Create
mkdocs.yml:
site_name: Payment Service Docs
nav:
- Overview: index.md
- Architecture: architecture.md
- API Reference: api.md
- Runbooks:
- Incident Response: runbooks/incidents.md
- Deployment: runbooks/deploy.md
plugins:
- techdocs-core
- Create
docs/index.mdβ your documentation
Backstage TechDocs Configuration
For development (basic, no pre-building):
techdocs:
builder: 'local'
generator:
runIn: 'local'
publisher:
type: 'local'
For production (pre-build and store in cloud storage):
techdocs:
builder: 'external'
generator:
runIn: 'docker'
publisher:
type: 'googleGcs' # or 'awsS3', 'azureBlobStorage'
googleGcs:
bucketName: my-techdocs-bucket
credentials:
$file: /path/to/service-account.json
Build docs in CI and publish to the bucket β Backstage serves them directly without rebuilding on every request.
Software Templates: Self-Service Scaffolding
Software Templates let developers create new services with your standards pre-baked: CI/CD pipelines, logging setup, code owners, Backstage catalog registration, everything.
A Minimal Template
# templates/new-nodejs-service/template.yaml
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: [recommended, nodejs]
spec:
owner: group:platform-team
type: service
parameters:
- title: Service Information
required: [name, description, owner]
properties:
name:
title: Service Name
type: string
pattern: '^[a-z0-9-]+$'
description:
title: Description
type: string
owner:
title: Owner Team
type: string
ui:field: OwnerPicker
ui:options:
catalogFilter:
kind: Group
- title: Repository
required: [repoUrl]
properties:
repoUrl:
title: Repository Location
type: string
ui:field: RepoUrlPicker
ui:options:
allowedHosts: [github.com]
steps:
- id: fetch-base
name: Fetch Base Template
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: ${{ parameters.repoUrl }}
defaultBranch: main
- 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 }}
The skeleton/ directory contains your template files with Nunjucks variable substitution.
Essential Plugins
GitHub Actions Plugin
Shows CI/CD status for each component directly in the catalog:
yarn --cwd packages/app add @backstage/plugin-github-actions
// packages/app/src/components/catalog/EntityPage.tsx
import { EntityGithubActionsContent } from '@backstage/plugin-github-actions';
const cicdContent = (
<EntitySwitch>
<EntitySwitch.Case if={isGithubActionsAvailable}>
<EntityGithubActionsContent />
</EntitySwitch.Case>
</EntitySwitch>
);
PagerDuty Plugin
yarn --cwd packages/app add @backstage/plugin-pagerduty
yarn --cwd packages/backend add @backstage/plugin-pagerduty-backend
Adds an on-call card and recent incidents to every serviceβs catalog page. Requires the pagerduty.com/service-id annotation on your entities.
Kubernetes Plugin
Shows pod status, resource usage, and recent deployments:
yarn --cwd packages/app add @backstage/plugin-kubernetes
yarn --cwd packages/backend add @backstage/plugin-kubernetes-backend
# app-config.yaml
kubernetes:
serviceLocatorMethod:
type: 'multiTenant'
clusterLocatorMethods:
- type: 'config'
clusters:
- url: https://k8s.internal
name: production
authProvider: 'serviceAccount'
serviceAccountToken: ${K8S_TOKEN}
caData: ${K8S_CA}
Cost Insights Plugin
Integrates with AWS Cost Explorer, GCP Billing, or other cost backends to surface cloud spend per team/service.
Production Deployment
Database: PostgreSQL
# app-config.production.yaml
backend:
database:
client: pg
connection:
host: ${POSTGRES_HOST}
port: 5432
user: ${POSTGRES_USER}
password: ${POSTGRES_PASSWORD}
database: backstage
ssl:
rejectUnauthorized: true
Authentication
Backstage supports OAuth via GitHub, Google, Okta, Azure AD, and others. For GitHub:
auth:
environment: production
providers:
github:
production:
clientId: ${AUTH_GITHUB_CLIENT_ID}
clientSecret: ${AUTH_GITHUB_CLIENT_SECRET}
signIn:
resolvers:
- resolver: usernameMatchingUserEntityName
Docker Deployment
FROM node:18-bookworm-slim
WORKDIR /app
COPY --chown=node:node yarn.lock package.json packages/backend/dist/bundle.tar.gz ./
RUN tar xzf bundle.tar.gz && rm bundle.tar.gz
RUN yarn install --frozen-lockfile --production --network-timeout 300000
ENV NODE_ENV production
EXPOSE 7007
CMD ["node", "packages/backend", "--config", "app-config.yaml", "--config", "app-config.production.yaml"]
Scaling Backstage Adoption
The technical setup is the easy part. Getting engineers to actually use it requires:
For catalog adoption:
- Make
catalog-info.yamlrequired in new service templates - Run a cataloging sprint β assign each team to add their services
- Gate deployments on catalog registration (enforce via CI)
For TechDocs adoption:
- Pre-populate docs stubs in your service template skeleton
- Make TechDocs the canonical source, deprecate other wikis
- Reward teams with complete, up-to-date docs (engineering metrics)
For template adoption:
- Start with templates for your most common service types
- Show the time savings: new service from scratch vs. template
- Track template usage in Backstageβs own metrics
Key Takeaways
- Backstageβs value compounds with adoption β 10 services catalogued is a demo, 200 services is a platform
- Start with the software catalog and TechDocs; add plugins as needs arise
- Software Templates drive ROI fastest β self-service reduces platform team toil
- Treat
catalog-info.yamlfiles like code: PR review, CI validation, ownership - Self-hosting means you control the data; the tradeoff is operational overhead
Backstage is not a quick install β itβs an investment in platform engineering. The payoff is an org where finding and understanding any service takes seconds, not Slack threads.
50 CLI Commands That Save Hours Every Week
The exact Git shortcuts, file search tricks, Docker commands, and productivity hacks that senior developers use daily. Compiled from 5+ years of shipping production code.
- Git mastery (10 cmds)
- File & search wizardry
- Docker & DevOps tricks
- Process & network tools
- Productivity boosters
- macOS & Linux install guide
No spam. 1 email/week max. Unsubscribe anytime.
Found this guide useful? Check out our free developer tools.
Affiliate disclosure: Some links below are affiliate links β we may earn a small commission at no extra cost to you. Learn more.
Recommended Tools & Resources
DigitalOcean
$200 credit for new users. Simple, affordable cloud hosting for developers.
GitHub Student Pack
Free access to 100+ developer tools. Perfect for students and new devs.
Vercel
Deploy frontend apps instantly. Free tier is generous for side projects.
DevPlaybook Products
Boilerplates, scripts & AI toolkits to 10x your dev workflow.