Getting Started
Stand up Backstage locally, register a service in the catalog, create a scaffolder template, deploy a generated service
Getting Started
This page stands up Backstage, the most popular OSS developer portal, and walks through the core loops: catalog, scaffolding, and TechDocs.
Prerequisites
- Node 18+, Yarn 4+
- Docker for the backend database
- Optional: GitHub PAT for catalog discovery and scaffolder integration
Create a Backstage App
npx @backstage/create-app@latest
# Follow prompts: app name 'my-idp'
cd my-idp
yarn install --immutable
yarn devBackstage starts at http://localhost:3000. You see the catalog (empty), TechDocs, and an API browser.
Register Your First Service
Each service has a catalog-info.yaml at its repo root:
# in github.com/my-org/checkout-service/catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: checkout-service
description: Handles the checkout flow
tags: [golang, payments]
annotations:
github.com/project-slug: my-org/checkout-service
backstage.io/techdocs-ref: dir:.
spec:
type: service
lifecycle: production
owner: team-payments
system: paymentsIn Backstage UI: Create → Register existing component → paste URL of the catalog-info.yaml. Backstage fetches it and adds the service.
Define the team in another entity:
# Catalog can hold groups + users too
apiVersion: backstage.io/v1alpha1
kind: Group
metadata: { name: team-payments }
spec:
type: team
profile:
displayName: Payments Team
email: payments@my-org.com
children: []Now the catalog entry links to a real team. Click "team-payments" and you see all services they own.
Discovery: Auto-Ingest Everything
Manually registering 200 services doesn't scale. Configure GitHub discovery:
# app-config.yaml
integrations:
github:
- host: github.com
token: ${GITHUB_TOKEN}
catalog:
providers:
github:
myCompany:
organization: 'my-org'
catalogPath: '/catalog-info.yaml'
schedule:
frequency: { minutes: 30 }
timeout: { minutes: 3 }Backstage scans your GitHub org every 30 minutes for catalog-info.yaml files. Self-registering catalog.
Build a Scaffolder Template
A template generates a fully-wired new service. Create a template repo:
templates/golang-service/
├── template.yaml # the template definition
└── skeleton/ # files that get rendered
├── catalog-info.yaml
├── README.md
├── Dockerfile
├── .github/workflows/ci.yaml
└── cmd/server/main.gotemplate.yaml:
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: golang-service-template
title: Go HTTP Service
description: Bootstrap a new Go HTTP service with CI, k8s, observability
spec:
owner: team-platform
type: service
parameters:
- title: Basics
required: [name, owner]
properties:
name:
type: string
title: Service name
pattern: '^[a-z][a-z0-9-]+$'
owner:
type: string
title: Owning team
ui:field: OwnerPicker
ui:options:
allowedKinds: [Group]
description:
type: string
title: Description
steps:
- id: fetch
name: Fetch and render
action: fetch:template
input:
url: ./skeleton
values:
name: ${{ parameters.name }}
owner: ${{ parameters.owner }}
description: ${{ parameters.description }}
- id: publish
name: Publish to GitHub
action: publish:github
input:
repoUrl: github.com?repo=${{ parameters.name }}&owner=my-org
defaultBranch: main
- id: register
name: Register in catalog
action: catalog:register
input:
repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
catalogInfoPath: '/catalog-info.yaml'
output:
links:
- title: Repo
url: ${{ steps.publish.output.remoteUrl }}
- title: Open in catalog
icon: catalog
entityRef: ${{ steps.register.output.entityRef }}skeleton/catalog-info.yaml:
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: ${{ values.name }}
description: ${{ values.description }}
annotations:
github.com/project-slug: my-org/${{ values.name }}
spec:
type: service
lifecycle: experimental
owner: ${{ values.owner }}Register the template:
# app-config.yaml
catalog:
locations:
- type: url
target: https://github.com/my-org/templates/blob/main/golang-service/template.yaml
rules: [{ allow: [Template] }]Now in Backstage: Create Component → Go HTTP Service → fill form → service repo + catalog entry. The form gives an engineer a complete, conventional starting point in minutes.
TechDocs: Docs Next to Code
Add mkdocs.yml and a docs/ directory to your service repo:
checkout-service/
├── catalog-info.yaml
├── mkdocs.yml
├── docs/
│ ├── index.md
│ ├── runbook.md
│ └── architecture.md
└── ...mkdocs.yml:
site_name: 'Checkout Service'
nav:
- Overview: index.md
- Runbook: runbook.md
- Architecture: architecture.md
plugins: [techdocs-core]Backstage's TechDocs reads markdown directly from the repo and renders it on the service page. No separate doc site to maintain.
Add Plugins
Backstage's value is plugins. Common ones:
- GitHub Actions — see workflow runs per service
- Kubernetes — pods/deployments per service
- Datadog / Prometheus / Grafana — embed dashboards
- PagerDuty — show on-call and incidents
- TechRadar — visualize tech adoption
- Sentry / Rollbar — error tracking
- Cost Insights — show service cost (FinOps integration)
- Scorecards — service maturity tracking (3rd party: Roadie, etc.)
Each plugin needs config and (usually) a token. Document carefully which plugins you've enabled.
Production Deployment
For real use:
- Database: PostgreSQL, not the dev SQLite.
- Auth: OIDC against your IdP (Okta, Google, Auth0, Keycloak).
- Container image: build with the Backstage Docker workflow.
- Persistence: TechDocs cache, scaffolder workspace.
- Backstage runs in your own cluster (typical Helm chart deploy).
yarn build-image
docker build -t my-backstage .
# Push to registry, deploy to clusterA full production setup is a 1-2 sprint platform engineering project — that's what makes Port and Cortex (SaaS) attractive alternatives.
What's Next
- Patterns — golden paths, scorecards, Crossplane integration, secrets
- Best Practices — platform-as-product, adoption, scaling team, pitfalls