# Backstage Adoption: From Demo to 80% Service Coverage in 6 Months

URL: https://www.devopsness.com/blog/backstage-adoption-from-demo-to-80-service-coverage-in-6-months-2026-04-23
Author: Kiril Urbonas
Published: 2026-04-23
Updated: 2026-04-23
Categories: Infrastructure
Tags: Kubernetes, CI/CD, GitHub Actions

Summary: We launched Backstage in October. Six months in, 80% of services are catalogued, on-boarding takes a third of the time, and we mostly know what owns what.

---
# Backstage Adoption: From Demo to 80% Service Coverage in 6 Months

Six months ago we deployed Backstage. Today: **127 of 158 services** in the catalog, **on-boarding time for new engineers down from 3 days to 1**, and we mostly know what owns what. Below is the rollout plan, the parts that worked, and the ones we'd skip.

## The Problem We Were Solving

Three separate incidents in two months had the same root cause: **nobody knew who owned the service that was breaking**. Slack threads chained through six teams before someone said "actually, that's been ours since Karen left." We needed a system of record for ownership, dependencies, and basic operational data.

We considered:
- A wiki page (had one; it rotted)
- Confluence space (had one; nobody updated it)
- A custom tool (estimated 6 months to build)
- **Backstage** (3 weeks to deploy a base version)

Backstage won.

## Week 1–2: Stand Up the Base

Standard Backstage Helm install on EKS, Postgres for backend, GitHub auth, GitHub provider for entity discovery.

```yaml
catalog:
  providers:
    github:
      providerId:
        organization: 'kirilurbonas'
        catalogPath: '/catalog-info.yaml'
        filters:
          branch: 'main'
          repository: '.*'
        schedule:
          frequency: { minutes: 30 }
          timeout: { minutes: 5 }
```

This auto-discovers any repo that contains a `catalog-info.yaml` file. Repos without it don't show up. That's the magic: **the catalog is sourced from the same repos as the code**.

## Week 3–6: Seed With High-Value Services

We didn't try to onboard everything at once. We picked **20 critical services** and wrote their `catalog-info.yaml` ourselves:

```yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: payments-api
  description: Customer-facing payments endpoint
  annotations:
    github.com/project-slug: kirilurbonas/payments-api
    pagerduty.com/integration-key: ${PAGERDUTY_KEY}
    grafana/dashboard-selector: "service=payments-api"
    sonarqube.org/project-key: payments-api
spec:
  type: service
  lifecycle: production
  owner: team-checkout
  system: checkout
  providesApis:
    - payments-public-api
  consumesApis:
    - billing-internal-api
  dependsOn:
    - resource:default/postgres-checkout-prod
    - resource:default/redis-checkout-prod
```

The annotations are the secret sauce. Each tells a Backstage plugin where to find the corresponding data:

- GitHub annotation → links and CI status appear automatically
- PagerDuty annotation → on-call rotation shown on the service page
- Grafana annotation → relevant dashboards embedded
- SonarQube annotation → code quality summary visible

A team doesn't write 200 lines of metadata. They write 20 lines of annotations and Backstage stitches the rest from existing tools.

## Week 7–10: The Snowball Phase

Once 20 services were in, we did something specific: **made onboarding new engineers go through Backstage**. New starters were given access to Backstage on day one and asked to find:

- The codebase for their team's main service
- The on-call schedule
- The runbook
- The Grafana dashboard

If any of those were missing for a service, they couldn't complete the task. The team responsible got a polite ping. Within 2 weeks of this practice, 50+ services had been added.

By week 10 we had 95 services in the catalog. The "who owns what" question became answerable for most things.

## Week 11–18: TechDocs

Backstage's TechDocs feature renders Markdown from your repos as a documentation site. We required every catalogued service to have:

- A `docs/` folder in the repo
- An `index.md` with a 2-paragraph "what is this and what does it do"
- A `runbook.md` (literally a copy-paste of the existing runbook in any format)

```yaml
metadata:
  annotations:
    backstage.io/techdocs-ref: dir:.
```

Six weeks later: documentation for 80% of services, accessible from the service's catalog page. Most of it was just **moving** existing docs into the right place; the value was in **discoverability**, not creation.

## Week 19–24: Software Templates

Backstage Software Templates let teams scaffold a new service through a UI:

```yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: typescript-service
  title: TypeScript Service
spec:
  parameters:
    - title: Service Info
      properties:
        name: { type: string, pattern: '^[a-z0-9-]+$' }
        team: { type: string, enum: [team-checkout, team-platform, team-ml] }
  steps:
    - id: fetch-base
      name: Fetch base template
      action: fetch:template
      input: { url: ./skeleton, values: { name: ${{ parameters.name }} } }

    - id: publish
      name: Publish to GitHub
      action: publish:github
      input:
        repoUrl: github.com?owner=kirilurbonas&repo=${{ parameters.name }}

    - id: register
      name: Register in catalog
      action: catalog:register
      input: { repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }} }
```

A new service from this template comes with:
- The right CI workflow
- A `catalog-info.yaml` already wired up
- A `docs/` skeleton
- Standard linting + security configuration
- Pagerduty rotation set to the chosen team

**New service spin-up time dropped from ~2 days to ~30 minutes.** The 30 minutes is mostly waiting for repo permissions to propagate.

## What's In The Catalog Now (Month 6)

| Entity Type | Count |
|---|---:|
| Service (Component) | 127 |
| Library (Component) | 41 |
| Website (Component) | 12 |
| API | 84 |
| Resource (DBs, queues) | 67 |
| System (logical grouping) | 14 |
| User (engineer) | ~80 |
| Group (team) | 18 |

Coverage: **127 services in catalog / 158 services total = 80%**. The missing 31 are mostly internal tools and "we'll get to it" services.

## What Worked

### 1. Annotations As Glue

The single biggest lever. We didn't ask teams to *fill out* metadata; we asked them to *point at existing tools*. Annotations link Backstage to the source of truth elsewhere. Updating PagerDuty updates Backstage; teams can't drift.

### 2. Onboarding As Forcing Function

Making "new engineer's day 1 task" require Backstage created social pressure to keep it current. No mandates, no audits — the bug surface was visible immediately.

### 3. Templates As The Carrot

```
Old way to start a service: 2 days
New way: 30 min, all integrations work
```

Once teams used the template once, they wanted *every* new service in Backstage so it could use templates. Pull > push.

### 4. Catalog Provider Auto-Discovery

We never had to ask a team to "add their service to Backstage." Adding a `catalog-info.yaml` to their repo did it automatically within 30 minutes. The barrier to adoption was as small as we could make it.

## What Didn't Work

### 1. Custom Plugins for Niche Tools

We tried writing a custom plugin to integrate an internal cost-allocation tool. Two weeks of work, broke on a Backstage upgrade, replaced with an annotation that links to a dashboard. **Lesson**: link to the source of truth instead of duplicating it.

### 2. Cost Tracking In The Catalog

We tried showing per-service cost on the catalog page. The data was always 24h stale, sometimes wrong, and led to "why does my service cost X" rabbit holes. Removed.

### 3. Forcing Every Library Into The Catalog

We initially required every internal library to have an entry. Most weren't owned by anyone in particular and the friction was higher than the value. Now we only catalog libraries with > 3 internal consumers.

### 4. The Default UI for Search

Out-of-the-box search prioritized by entity name, not relevance. Teams searching "payment" got the `payments-tests` library before `payments-api`. We tuned the search index weights to fix this; took longer than expected.

## Numbers Worth Sharing

| Metric | Pre-Backstage | Month 6 |
|---|---|---|
| New engineer time-to-productive | 3 days | 1 day |
| "Who owns this service?" question | hours/days | minutes |
| New service spin-up | ~2 days | ~30 min |
| Services with documentation | ~40% | ~80% |
| Services with runbook | ~25% | ~75% |
| Time spent maintaining catalog | n/a | ~3 hr/week (1 person) |

## What's Still Hard

### 1. Catalog Drift

Despite our best efforts, ~5% of catalog entries are wrong at any given time. Wrong owners (team renamed; catalog says old name), wrong APIs (deprecated, not removed). We have a quarterly "catalog hygiene week" to clean up.

### 2. Bridging Backstage and Existing Wikis

Our Confluence and Backstage docs aren't fully merged. New stuff goes to TechDocs; old stuff stays in Confluence with cross-links. Not ideal but acceptable.

### 3. Team Identity Source

Backstage Groups should map to teams. Our HR system, PagerDuty, GitHub Teams, and Slack all have *slightly* different ideas of what "team-checkout" is. We treat HR as truth and reconcile downstream — error-prone.

## Best Practices We'd Recommend

1. **Start with auto-discovery via `catalog-info.yaml`** in repos. Don't require humans to register entities.
2. **Heavy use of annotations.** Link to existing tools rather than duplicating their data in Backstage.
3. **Make onboarding require the catalog.** Social pressure beats mandates.
4. **Build software templates early.** Templates are the value-prop that pulls teams in.
5. **Don't fight existing wikis.** Let them stay; cross-link from Backstage. Forcing migration is a year of pain for marginal benefit.
6. **Pick a catalog hygiene cadence.** Quarterly works for us; weekly is overkill.
7. **Plug into your IDP, not just SSO.** Teams, identities, and groups should reflect HR truth.

## When To Skip Backstage

- **< 20 services**: a wiki page is fine.
- **No platform team**: the maintenance, while manageable, is non-zero.
- **Locked-down vendor stack with no integrations**: Backstage's value is in the plugin ecosystem.

For everyone else, the question isn't "should we use Backstage" but "what would take its place if we don't." If the answer is "nothing currently does this and we feel it," start.

## What I'd Do Differently

- **Onboard the platform team first** with realistic ownership data. We let some things slip because the platform team's own services were the last to get full metadata.
- **Avoid custom plugins for at least 6 months.** Use what's there.
- **Set up catalog hygiene automation early.** A bot that pings stale entries weekly catches drift before it's painful.

Backstage doesn't fix culture problems. It surfaces them. The "who owns this" question still requires teams that *can* own things; Backstage just makes the answer findable when those teams exist.

