Backstage Developer Portal Setup Guide
Backstage developer portal has become the de facto standard for internal developer platforms. Originally created at Spotify, this open-source platform now powers developer experience at thousands of organizations. It provides a unified service catalog, documentation hub, and extensible plugin ecosystem that eliminates the fragmentation of internal tooling.
This guide walks you through building a production-ready Backstage instance with a populated service catalog, custom plugins, and software templates. Whether you are starting a platform engineering initiative or consolidating existing tools, this guide covers the practical steps to deliver value quickly.
Architecture Overview
Backstage consists of three core components: the frontend app (React), the backend (Node.js), and a PostgreSQL database for persistent storage. The plugin architecture allows teams to extend functionality without modifying the core platform.
# Create a new Backstage app
npx @backstage/create-app@latest my-portal
cd my-portal
# Project structure
# ├── app-config.yaml # Main configuration
# ├── app-config.production.yaml
# ├── packages/
# │ ├── app/ # Frontend (React)
# │ └── backend/ # Backend (Node.js)
# └── plugins/ # Custom plugins
Configuring the Service Catalog
The service catalog is the heart of any Backstage developer portal. It provides a centralized registry of all software components, APIs, resources, and their ownership. Moreover, it automatically discovers and ingests catalog entities from your Git repositories.
# app-config.yaml
catalog:
import:
entityFilename: catalog-info.yaml
pullRequestBranchName: backstage-integration
rules:
- allow: [Component, System, API, Resource, Location, Group, User]
locations:
# GitHub org discovery
- type: github-discovery
target: https://github.com/my-org/*/blob/main/catalog-info.yaml
# Static locations
- type: url
target: https://github.com/my-org/backstage-catalog/blob/main/all-systems.yaml
processors:
githubOrg:
providers:
- target: https://github.com
apiBaseUrl: https://api.github.com
orgs: ['my-org']
Each service registers itself with a catalog-info.yaml file in its repository root:
# catalog-info.yaml in each service repo
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: payment-service
description: Handles payment processing and billing
annotations:
github.com/project-slug: my-org/payment-service
backstage.io/techdocs-ref: dir:.
pagerduty.com/service-id: PABCDEF
grafana/dashboard-selector: "payment-*"
tags:
- java
- spring-boot
- payments
links:
- url: https://grafana.internal/d/payment
title: Grafana Dashboard
icon: dashboard
spec:
type: service
lifecycle: production
owner: team-payments
system: billing-platform
providesApis:
- payment-api
consumesApis:
- user-api
- notification-api
dependsOn:
- resource:default/payments-db
- resource:default/payments-redis
Building Custom Plugins
Backstage plugins extend the portal with new features. Furthermore, the plugin SDK provides a structured approach to building both frontend and backend extensions:
# Generate a new plugin
cd my-portal
npx @backstage/cli new --select plugin
# Enter plugin ID: cost-insights
// plugins/cost-insights/src/components/CostDashboard.tsx
import React from 'react';
import { useApi, configApiRef } from '@backstage/core-plugin-api';
import { Table, TableColumn } from '@backstage/core-components';
interface ServiceCost {
name: string;
monthlyCost: number;
trend: number;
owner: string;
}
export const CostDashboard = () => {
const [costs, setCosts] = React.useState<ServiceCost[]>([]);
const config = useApi(configApiRef);
React.useEffect(() => {
fetch('/api/cost-insights/services')
.then(res => res.json())
.then(data => setCosts(data));
}, []);
const columns: TableColumn<ServiceCost>[] = [
{ title: 'Service', field: 'name' },
{ title: 'Monthly Cost', field: 'monthlyCost',
render: row => `${row.monthlyCost.toFixed(2)}` },
{ title: 'Trend', field: 'trend',
render: row => row.trend > 0
? `+${row.trend}%`
: `${row.trend}%` },
{ title: 'Owner', field: 'owner' },
];
return (
<Table
title="Service Cost Overview"
columns={columns}
data={costs}
options={{ pageSize: 20, search: true }}
/>
);
};
Software Templates for Golden Paths
Software templates codify best practices into self-service project scaffolding. As a result, developers can create new services, libraries, or infrastructure components that automatically follow organizational standards:
# templates/spring-service/template.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: spring-boot-service
title: Spring Boot Microservice
description: Create a production-ready Spring Boot microservice
spec:
owner: team-platform
type: service
parameters:
- title: Service Details
required: [name, owner, description]
properties:
name:
title: Service Name
type: string
pattern: '^[a-z][a-z0-9-]*