How to Set Up Backstage Internal Developer Portal from Scratch in 2026
Backstage is the open-source Internal Developer Portal (IDP) from Spotify, now used by Netflix, LinkedIn, and thousands of engineering teams. This step-by-step guide shows you how to deploy it, add your services, and integrate it with GitHub and Kubernetes.
If you have worked at a company with more than 30 engineers, you have felt the pain: nobody knows which services exist, where they are deployed, who owns them, or what they depend on. Documentation lives in 15 different places. New engineers spend their first two weeks just trying to understand what runs in production.
Backstage was built to solve this. Originally created at Spotify to manage thousands of microservices, it was open-sourced in 2020 and is now one of the fastest-growing infrastructure tools in the industry. Netflix, LinkedIn, American Airlines, and thousands of other companies use it as the single interface where engineers can find anything related to their services.
This guide walks you through setting up a production-ready Backstage instance from scratch — creating the app, connecting it to GitHub, registering services in the catalog, and deploying it on Kubernetes.
What Backstage Actually Does
Before setting it up, it is worth understanding what Backstage is — because the name "internal developer portal" does not fully capture it.
Backstage has three core features:
Software Catalog: A centralized registry of all your services, libraries, websites, APIs, and infrastructure. Each item in the catalog is described by a YAML file (called a catalog-info.yaml) that lives in the service's repository. The catalog shows owners, dependencies, documentation links, CI/CD status, and any other metadata you want to attach.
Software Templates (Scaffolder): A system for creating new services from templates. Instead of engineers copying a starter repository and making 20 manual changes, they fill out a form in Backstage and a new repository is created, configured, and registered in the catalog automatically. This is the feature that saves the most time at scale.
TechDocs: Documentation that lives alongside code (as Markdown files in the same repository) and is rendered inside Backstage. The idea is to kill the documentation wiki by making docs a natural part of the service.
Beyond these three, Backstage has a plugin system with hundreds of community plugins: Kubernetes cluster status, GitHub Actions pipelines, PagerDuty incidents, Datadog dashboards, cost data from cloud providers. Almost anything you want to see about a service can be surfaced inside Backstage.
Prerequisites
Before starting, make sure you have:
- Node.js 20.x or later (
node --version) - Yarn (
npm install -g yarn) - Docker (for local testing)
- A Kubernetes cluster (for deployment — DigitalOcean Kubernetes is straightforward to set up)
- A GitHub account and personal access token (or GitHub App)
kubectlconfigured to connect to your clusterhelminstalled
Step 1: Create a New Backstage Application
Backstage provides a CLI that scaffolds a new application with all the defaults. This creates a fully working Backstage instance that you own and can customize.
# Create a new Backstage app
npx @backstage/create-app@latest
# When prompted, give it a name
# Name: my-developer-portalThis will create a directory my-developer-portal/ with the full Backstage application. The structure looks like this:
my-developer-portal/
├── app-config.yaml # Main configuration file
├── packages/
│ ├── app/ # Frontend (React)
│ └── backend/ # Backend (Node.js)
├── plugins/ # Custom plugins go here
└── package.json
The most important file for our purposes is app-config.yaml. Almost all Backstage configuration happens here.
Step 2: Configure GitHub Integration
Backstage reads catalog-info.yaml files from your GitHub repositories. To do this, it needs a GitHub token with repository access.
Create a GitHub Personal Access Token
Go to GitHub → Settings → Developer Settings → Personal Access Tokens → Tokens (classic). Create a token with the following scopes:
repo(full repository access)read:org(read organization data)read:user
Keep this token — you will need it in the next step.
Update app-config.yaml
Open app-config.yaml and add the GitHub integration block:
# app-config.yaml
app:
title: My Developer Portal
baseUrl: http://localhost:3000
organization:
name: Your Company Name
backend:
baseUrl: http://localhost:7007
listen:
port: 7007
database:
client: better-sqlite3
connection: ':memory:'
integrations:
github:
- host: github.com
token: ${GITHUB_TOKEN}
auth:
providers:
github:
development:
clientId: ${GITHUB_CLIENT_ID}
clientSecret: ${GITHUB_CLIENT_SECRET}
catalog:
providers:
github:
defaultOrg:
organization: your-github-org
catalogPath: /catalog-info.yaml
filters:
branch: main
schedule:
frequency: { minutes: 30 }
timeout: { minutes: 3 }
rules:
- allow: [Component, System, API, Resource, Location, Template]Set Environment Variables
export GITHUB_TOKEN=your-github-token
export GITHUB_CLIENT_ID=your-github-oauth-app-client-id
export GITHUB_CLIENT_SECRET=your-github-oauth-app-secretFor GitHub OAuth (which enables GitHub login in Backstage): go to GitHub → Settings → Developer Settings → OAuth Apps → New OAuth App. Set the callback URL to http://localhost:7007/api/auth/github/handler/frame.
Step 3: Run Backstage Locally
Start the development server to verify everything is working:
cd my-developer-portal
yarn install
yarn devThis starts both the frontend (port 3000) and backend (port 7007). Open http://localhost:3000 in your browser. You should see the Backstage UI with an empty catalog.
Step 4: Register Your First Service
For Backstage to know about a service, you add a catalog-info.yaml file to the service's repository. This is the fundamental concept of Backstage's Software Catalog — the catalog is distributed across your repositories, not centralized in a database you maintain manually.
catalog-info.yaml Structure
Here is a real-world example for a Node.js API service:
# catalog-info.yaml (lives in the root of your service repo)
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: payment-api
description: Handles payment processing and subscription management
annotations:
github.com/project-slug: your-org/payment-api
backstage.io/techdocs-ref: dir:.
backstage.io/kubernetes-id: payment-api
tags:
- nodejs
- api
- payments
links:
- url: https://grafana.yourdomain.com/d/payment-api
title: Grafana Dashboard
icon: dashboard
- url: https://docs.yourdomain.com/payment-api
title: External Docs
spec:
type: service
lifecycle: production
owner: team-payments
system: billing-platform
dependsOn:
- component:postgres-payments
- component:stripe-integration
providesApis:
- payment-api-v1The key fields:
kind: Component (a deployable service), API, System, Resource, or User/Groupspec.owner: The team responsible for this service (must match a Group in your catalog)spec.lifecycle:production,experimental, ordeprecatedspec.dependsOn: Other catalog entities this service depends on (used to build the dependency graph)
Register in Backstage
After adding catalog-info.yaml to your repository, register it in Backstage:
- Go to
http://localhost:3000/catalog-import - Enter the URL to your catalog-info.yaml:
https://github.com/your-org/payment-api/blob/main/catalog-info.yaml - Click Analyze, then Import
If you configured the GitHub auto-discovery in Step 2, Backstage will automatically find catalog-info.yaml files across your organization every 30 minutes — you only need to manually import when you first set up auto-discovery.
Step 5: Add the Kubernetes Plugin
The Kubernetes plugin lets engineers see pod status, deployment health, and recent events for their service directly in Backstage — without needing kubectl access.
Install the Plugin
# Install frontend plugin
cd packages/app
yarn add @backstage/plugin-kubernetes
# Install backend plugin
cd ../backend
yarn add @backstage/plugin-kubernetes-backendConfigure the Backend
In packages/backend/src/index.ts, add:
import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend';
// Inside the main function:
const { router } = await KubernetesBuilder.createBuilder({
logger,
config,
discovery,
permissions,
}).build();
apiRouter.use('/kubernetes', router);Add Cluster Configuration
In app-config.yaml:
kubernetes:
serviceLocatorMethod:
type: multiTenant
clusterLocatorMethods:
- type: config
clusters:
- url: https://your-cluster-api-endpoint
name: production
authProvider: serviceAccount
serviceAccountToken: ${K8S_SERVICE_ACCOUNT_TOKEN}
caData: ${K8S_CA_DATA}
skipTLSVerify: falseCreate a ServiceAccount in your cluster for Backstage:
apiVersion: v1
kind: ServiceAccount
metadata:
name: backstage
namespace: backstage
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: backstage-read
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch"]For Kubernetes to associate pods with a Backstage component, add this label to your Deployments:
labels:
backstage.io/kubernetes-id: payment-apiStep 6: Build the Docker Image
When you are ready to move beyond local development, build a production Docker image:
# Build the backend image
yarn build:backend --config ../../app-config.yaml
# Build the Docker image
docker build . -f packages/backend/Dockerfile --tag backstage:1.0.0The default Dockerfile in packages/backend/Dockerfile is production-ready. It is a multi-stage build that installs only production dependencies.
Step 7: Deploy to Kubernetes
Create a Kubernetes namespace and deploy Backstage.
Kubernetes Manifests
# backstage-deployment.yaml
apiVersion: v1
kind: Namespace
metadata:
name: backstage
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backstage
namespace: backstage
spec:
replicas: 1
selector:
matchLabels:
app: backstage
template:
metadata:
labels:
app: backstage
spec:
serviceAccountName: backstage
containers:
- name: backstage
image: your-registry/backstage:1.0.0
ports:
- containerPort: 7007
env:
- name: GITHUB_TOKEN
valueFrom:
secretKeyRef:
name: backstage-secrets
key: GITHUB_TOKEN
- name: POSTGRES_HOST
value: "backstage-postgres"
- name: POSTGRES_PORT
value: "5432"
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: backstage-secrets
key: POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: backstage-secrets
key: POSTGRES_PASSWORD
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
---
apiVersion: v1
kind: Service
metadata:
name: backstage
namespace: backstage
spec:
selector:
app: backstage
ports:
- port: 80
targetPort: 7007
type: ClusterIPCreate Secrets
kubectl create secret generic backstage-secrets \
--namespace backstage \
--from-literal=GITHUB_TOKEN=your-token \
--from-literal=POSTGRES_USER=backstage \
--from-literal=POSTGRES_PASSWORD=your-passwordApply and Verify
kubectl apply -f backstage-deployment.yaml
# Check pod is running
kubectl get pods -n backstage
# Follow logs
kubectl logs -n backstage -l app=backstage -fStep 8: Switch to PostgreSQL for Production
The default in-memory SQLite database loses all data on pod restart. For production, use PostgreSQL.
Update app-config.yaml:
backend:
database:
client: pg
connection:
host: ${POSTGRES_HOST}
port: ${POSTGRES_PORT}
user: ${POSTGRES_USER}
password: ${POSTGRES_PASSWORD}
database: backstage_plugin_catalogYou can deploy PostgreSQL using the Bitnami Helm chart:
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install backstage-postgres bitnami/postgresql \
--namespace backstage \
--set auth.username=backstage \
--set auth.password=your-secure-password \
--set auth.database=backstage_plugin_catalogWhat to Build Next
Once your basic Backstage instance is running, here is the prioritized list of additions that provide the most value:
-
Software Templates: Create a template for new service creation so engineers spin up repositories with CI/CD, catalog-info.yaml, and TechDocs pre-configured.
-
TechDocs: Move your service documentation to Markdown files in repositories, rendered inside Backstage. Run
mkdocslocally to preview, then configure the TechDocs plugin to build and serve docs. -
GitHub Actions Plugin: Show CI/CD pipeline status for each service inside its Backstage page.
-
Cost Plugin: Connect your cloud cost data so teams can see spending per service directly in the catalog.
Learning More
Platform engineering and Internal Developer Portals are one of the fastest-growing areas in DevOps. Backstage is at the center of it. If you want structured learning on platform engineering concepts and Kubernetes — the foundation you need to manage Backstage well — KodeKloud's platform engineering path is one of the best structured resources available.
For hosting your Backstage instance, DigitalOcean's managed Kubernetes is worth considering — it is significantly cheaper than EKS or GKE for small teams, and Backstage's resource requirements (1 CPU, 1GB RAM) fit comfortably in a small DOKS cluster.
Conclusion
Backstage takes a few hours to set up and a few weeks to populate meaningfully. The investment pays off when your fifth engineer does not have to ask where the payment service lives, who owns it, and why it is throwing errors — because all of that is in the catalog.
The pattern that works: start with your five most critical services, get the basics right (catalog, GitHub integration, Kubernetes plugin), and then let teams populate the catalog themselves. Backstage works best as a team-owned tool, not a top-down mandate.
If your organization is managing more than 10 services, this is one of the highest-leverage infrastructure investments you can make in 2026.
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
AI Coding Assistants Will Change DevOps — But Not in the Way You Think
GitHub Copilot, Cursor, and Claude are already writing infrastructure code. But the real disruption isn't replacing DevOps engineers — it's reshaping what the job actually is.
ArgoCD vs Flux vs Jenkins — GitOps Comparison 2026
A deep-dive comparison of the three most popular GitOps and CI/CD tools — ArgoCD, Flux CD, and Jenkins. Learn which one fits your team, use case, and Kubernetes setup.
Best DevOps Tools Every Engineer Should Know in 2026
A comprehensive guide to the essential DevOps tools for containers, CI/CD, infrastructure, monitoring, and security — curated for practicing engineers.