reduce engineering discovery friction with a catalog-first portal
Backstage developer portal: service catalog, plugins, and golden-path templates at scale
12 min read
When ownership lives in Slack threads and deploy docs rot in Confluence, delivery slows. This guide shows how Backstage unifies discovery through catalog-info.yaml, custom plugins, scaffolder templates, and catalog-first rollout practices.
Why scattered tools create an organizational discovery tax
Past a handful of teams, engineers spend hours finding the right repository, identifying service owners, or locating deployment runbooks buried in wikis. Onboarding stretches from days to weeks. Orphaned services accumulate because ownership was never recorded. Most organizations respond with ad hoc wikis, spreadsheets, and ask-in-Slack culture that does not scale. The cost is invisible but steady: every developer repeats the same discovery work, and platform teams cannot enforce standards they cannot see.
Backstage as connective tissue, not a CI/CD replacement
Backstage, open-sourced by Spotify and hosted as a CNCF Incubating project, is an extensible internal developer portal. It does not replace GitHub Actions, Prometheus, PagerDuty, or Kubernetes. It aggregates them so developers start from one place. Four pillars matter in practice. The Software Catalog registers services, libraries, APIs, and resources from catalog-info.yaml files in Git. Software Templates (the scaffolder) create new repositories from golden-path blueprints. TechDocs builds documentation from MkDocs sources in the service repo. A broad plugin ecosystem surfaces Kubernetes, GitHub, Terraform, and observability data without duplicating those systems. Production deployments typically run Backstage as a Node.js app with PostgreSQL for portal state, plus Elasticsearch, OpenSearch, or the PostgreSQL search module for catalog search, authenticated through your existing OIDC provider.
Register every service with catalog-info.yaml
The catalog entity is the contract between product teams and platform engineering. Commit catalog-info.yaml at the repository root so Backstage can ingest ownership, lifecycle, dependencies, and deep links to dashboards or runbooks. Reference other entities with typed relations such as resource:default/postgres-payment-db rather than free-form strings. A single validated file powers search, dependency graphs, TechDocs entry points, and plugin context for each Component page.
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: payment-service
description: Handles payment processing and refunds
annotations:
github.com/project-slug: myorg/payment-service
pagerduty.com/service-id: P123456
backstage.io/techdocs-ref: dir:.
tags:
- java
- payments
- tier-1
links:
- url: https://grafana.internal/d/payment-service
title: Grafana Dashboard
spec:
type: service
lifecycle: production
owner: group:payments-team
system: commerce-platform
providesApis:
- payment-api
dependsOn:
- resource:default/postgres-payment-dbExtend the portal with plugins and scaffolder templates
Plugins are React front-end modules backed by optional backend routes. Use them to read from existing APIs—CI metrics, deployment frequency, cost tags—instead of creating parallel data stores. Software Templates are Template custom resources registered in the catalog. They collect parameters, fetch a skeleton directory, render files such as catalog-info.yaml, and publish to GitHub or GitLab. The skeleton snippet below is what the template injects; the Template manifest defines the wizard and actions.
import React from 'react';
import { useApi } from '@backstage/core-plugin-api';
import { deploymentMetricsApiRef } from '../api';
import { Progress } from '@backstage/core-components';
import { useAsync } from 'react-use';
export const DeploymentFrequency = ({ entity }) => {
const api = useApi(deploymentMetricsApiRef);
const { value, loading } = useAsync(
() => api.getDeploymentFrequency(entity.metadata.name),
[entity]
);
if (loading) return <Progress />;
return (
<div>
<h3>Deployment Frequency (last 30 days)</h3>
<p>{value?.count ?? 'N/A'} deployments</p>
<p>Average interval: {value?.avgInterval ?? 'N/A'}</p>
</div>
);
};apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: microservice
title: New microservice
description: Scaffold a service with catalog registration and CI wiring
spec:
owner: group:platform-team
type: service
parameters:
- title: Service metadata
required: [name, owner, system, org]
properties:
name:
title: Name
type: string
description:
title: Description
type: string
owner:
title: Owner group
type: string
system:
title: System
type: string
org:
title: GitHub org
type: string
default: myorg
steps:
- id: fetch
name: Fetch skeleton
action: fetch:template
input:
url: ./skeleton
values:
name: ${{ parameters.name }}
description: ${{ parameters.description }}
owner: ${{ parameters.owner }}
system: ${{ parameters.system }}
org: ${{ parameters.org }}
- id: publish
name: Publish to GitHub
action: publish:github
input:
repoUrl: github.com?owner=${{ parameters.org }}&repo=${{ parameters.name }}
output:
links:
- title: Repository
url: ${{ steps['publish'].output.remoteUrl }}
---
# templates/microservice/skeleton/catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: ${{ values.name }}
description: ${{ values.description }}
annotations:
github.com/project-slug: ${{ values.org }}/${{ values.name }}
spec:
type: service
lifecycle: experimental
owner: ${{ values.owner }}
system: ${{ values.system }}Rollout practices platform teams should enforce
Start with catalog coverage, not custom UI. Enforce catalog-info.yaml through CI validation on every merge so the portal cannot drift from Git truth. Maintain three to five scaffolder templates as golden paths; a broken template erodes trust faster than no template. Require owner on every entity and lint for it. Co-locate TechDocs under docs/ with mkdocs.yml so documentation ships with code. Integrate SSO and the permission framework before opening template registration to broad write access. Run production Backstage on Kubernetes with PostgreSQL, a supported search backend, and at least two replicas. Version templates in Git with PR review like application code. Measure adoption—catalog registration percentage, template usage, onboarding time—not plugin count. A developer portal is infrastructure for engineering velocity: start small, automate catalog ingestion ruthlessly, and treat operational clarity as the outcome that matters.
Backstage is often the UI layer on top of the deployment abstractions described in our internal developer platform guide.
Catalog entries should link to healthy delivery telemetry; pair portal rollout with the release pipeline bottlenecks framework.
