# Internal Developer Platforms: Backstage in Practice

URL: https://www.devopsness.com/blog/internal-developer-platforms-backstage-in-practice
Author: Kiril Urbonas
Published: 2026-05-06
Updated: 2026-10-31
Categories: DevOps
Tags: Idp, Backstage, Developer Experience, Platform Engineering, DevOps

Summary: We adopted Backstage for service catalogs and templates. What works, what was over-engineered for our size, and what we'd do differently.

---
# Internal Developer Platforms: Backstage in Practice

Backstage is worth it once you have roughly 20 or more engineers and nobody can answer "who owns this service" in 30 seconds. Use the software catalog and templates; skip deep self-service plugins. Under about 15 engineers, a `services.md` in a repo does the job for far less upkeep.

> [!VERIFIED]
> Backstage 1.56 (v1.56.2, the latest stable release on GitHub), against the backstage.io versioning policy, catalog descriptor format and software templates docs. Checked October 2026.

We adopted Backstage about 18 months ago to give engineers a single place to find services, scaffold new ones, and read internal docs. The promise, one portal for "everything about your service", partially landed. Some pieces are useful, some were over-engineered for our size, and one we abandoned.

## What "internal developer platform" actually means

The term is overloaded. In practice, an IDP is some combination of:

1. **Service catalog:** a registry of every service, with metadata: owning team, runbook, dashboard links, dependencies.
2. **Software templates:** scaffolds for creating new services with the right defaults baked in.
3. **TechDocs / documentation hub:** central docs living next to the code.
4. **Workflow integrations:** links into CI/CD, deployment, on-call.
5. **Self-service plugins:** provision a database, request a feature flag, etc.

Backstage covers #1 to #4 well. #5 is where teams over-invest and where most "IDP project failed" stories come from.

## What we actually use

Of the five categories, two earn their place daily:

**Service catalog (1).** Every service has a `catalog-info.yaml` in its repo. It declares ownership, dependencies, lifecycle (production / experimental / deprecated), and links (runbook, dashboard, on-call). The catalog UI lets an engineer find "who owns this service" in one click instead of grepping commit history. Daily-used.

**Software templates (2).** A Backstage template scaffolds every new service repo with our [golden-path boilerplate](/blog/platform-engineering-golden-paths) (details below). Engineers run it from the Backstage UI; about 5 minutes from "I need a new service" to "PR open with all the boilerplate."

The rest:

- **TechDocs (3):** we use it lightly. Most docs still live in Notion / wiki. Backstage TechDocs is great in theory; in practice maintaining two docs systems became a burden, and engineers preferred Notion's editing UX.
- **Workflow integrations (4):** works but mostly cosmetic. Plugins for CI status, deployment state. Useful but not revolutionary; engineers also have direct access to the underlying tools.
- **Self-service plugins (5):** we tried building a few. Most weren't worth the effort. More on this below.

## What we abandoned: deep self-service plugins

The promise: "engineers can self-serve database creation, feature-flag toggling, secret rotation, etc., all from Backstage." Sounds great.

Reality: each plugin needed:
- An integration with the target system (DB provisioning needs Terraform; flag toggling needs the flag platform API, and [running flags in production](/blog/feature-flags-in-production-operational-reality) already has its own UI)
- An approval / authorization layer (who can provision a production DB?)
- Audit logging
- Error handling when the integration breaks

Each was a small project. We built two (DB provisioning, S3 bucket creation) and used them maybe ten times each before realizing the engineers preferred the CLI tools they already had. The Backstage UI was a thin layer; the actual work was the integrations behind it.

We retired both plugins. New self-service work goes into the CLI tools directly. Backstage links to the docs.

## Operational reality

Backstage is a real Node.js app you have to run. We deployed it on EKS:

- ~3 pods, ~1 CPU and 1 GB RAM each. Modest.
- Postgres backend for the catalog state. Same RDS cluster as one of our internal apps.
- Search backend (we use Elasticsearch; the in-memory Lunr engine or Postgres are options for small catalogs).
- GitHub auth integration for SSO.

Upgrades are the biggest pain. Per the project's versioning policy, the main release line ships monthly (on the Tuesday before the third Wednesday) and the `next` line weekly, and plugin APIs change often. We upgrade quarterly, treating it like a real maintenance task. Some quarters we skip a release because the changelog is heavy.

Operational time: ~6 hours per quarter on upgrades + occasional plugin issues. Manageable but real.

## Service catalog as code

The pattern that worked: catalog entries live in the same repo as the code. Each service has `catalog-info.yaml`:

```yaml
# catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: checkout-api
  description: Customer checkout API
  annotations:
    backstage.io/source-location: url:https://github.com/company/checkout-api
    grafana/dashboard-selector: "tag:checkout-api"
    pagerduty.com/service-id: P12345
    github.com/project-slug: company/checkout-api
spec:
  type: service
  lifecycle: production
  owner: team-checkout
  system: checkout
  dependsOn:
    - component:default/checkout-db
    - component:default/payments-api
```

Backstage scans configured repos and ingests these. Updates take ~5 minutes to propagate. Engineers update catalog-info.yaml in PRs alongside code changes; ownership info stays current automatically.

What we don't do: manual entry of services through the UI. That drifts. Repo-based catalog declarations stay in sync with reality.

## Templates that earn their place

Our Backstage software template for a new web service generates:

- Repo with our standard directory structure
- Dockerfile (multi-stage, distroless base)
- GitHub Actions workflow (test, build, push image)
- Argo CD Application manifest for staging + prod
- catalog-info.yaml with sensible defaults
- README skeleton with required sections
- OpenTelemetry init code
- Health check endpoints

The engineer answers about 5 questions (name, team, language) in the UI, reviews the generated PR, merges, and gets to feature work.

The template lives in version control. When we change our standards (new logging library, new image base), we update the template; existing services get fixed via their own PRs as they update.

Time from "I want a new service" to "running in staging" used to be ~half a day of copying from another service. Now it's 30 minutes including code review of the generated PR.

## What we wish we'd done differently

Two things in hindsight:

**Started with just the catalog.** Trying to do catalog + templates + tech docs + plugins simultaneously meant none of them got polished. Catalog alone, deeply, then templates, then add what's needed. Phased adoption beats wholesale launch.

**Treated TechDocs as optional.** Our migration of all docs into TechDocs stalled because the friction of moving off Notion outweighed the value. Linking to existing docs from Backstage would have given the same outcome for less effort.

## What it's worth

If your team is small (< 5 engineers), Backstage is overkill. A README and a `services.md` in a docs repo does the job.

For 20+ engineers, multiple services, and ownership questions like "who do I page for X", Backstage starts paying back. The catalog and templates are the highest-ROI features.

For 100+ engineers, true platform-engineering territory, Backstage is one of several tools you'd consider (Port, Cortex, custom). The operational cost is justified.

We're around 30 engineers across 5 teams. Backstage is net-positive for us but barely. If we were 15 engineers we'd probably skip it.

## Alternatives

- **Port:** managed IDP, less customizable, less ops overhead. Good if you don't want to run Backstage.
- **Cortex:** similar, more focused on service maturity scoring.
- **Just a git repo:** `services.md` with a YAML table. Sounds primitive; works fine for many teams. We did this for 18 months before Backstage.
- **Custom internal portal:** some teams build their own. Real investment; worth it only if Backstage's defaults are wrong for you in specific ways.

## The decision, concretely

- **Can engineers find a service owner in 30 seconds today?** Then the catalog adds little; skip Backstage.
- **Adopting anyway?** Catalog plus one template first, `catalog-info.yaml` in-repo, no plugins on day one.
- **Thinking about self-service plugins?** Only where they beat the CLI engineers already use.

## Questions people ask

### Is Backstage worth it for a small team?

Usually not below about 15 engineers. Backstage is a Node.js app with a Postgres backend that you deploy, secure and upgrade on a monthly release line. If your team can already answer "who owns this service" from a `services.md` file or a shared YAML table, the catalog adds little. The payback starts when ownership questions slow down incidents and new services take half a day to scaffold.

### What is the difference between Backstage and an internal developer platform?

An internal developer platform is the whole set of paved roads: catalog, templates, CI/CD, provisioning and docs. Backstage is a framework for the portal in front of it. It gives you a software catalog, software templates, TechDocs and a plugin system, but the real platform work (Terraform modules, pipelines, access control) still lives behind it and has to be built either way.

### How much effort does running Backstage take?

For us, about 6 hours a quarter on upgrades and plugin breakage, plus three small pods and a Postgres database. The cost grows with every plugin you run and every frontend customization. Teams that build many custom self-service plugins often find Backstage becomes someone's full-time job, which is the outcome to plan against from the start.

## The call we'd make

Start with the catalog, declared in `catalog-info.yaml` in each repo, plus one good software template, and stop there until engineers ask for more. Backstage used for the things it is good at is a useful platform layer; used to its full advertised potential, it becomes a small platform team's full-time job. If the catalog is the piece worth investing in, the harder problem is getting engineers to trust it, which we cover in [Backstage software catalog: getting adoption past the demo](/blog/backstage-software-catalog-adoption).

## Sources

- [Backstage Docs: Versioning policy and release cadence](https://backstage.io/docs/overview/versioning-policy/)
- [Backstage Docs: Descriptor format of catalog entities](https://backstage.io/docs/features/software-catalog/descriptor-format/)
- [Backstage Docs: Software templates](https://backstage.io/docs/features/software-templates/)
- [Backstage releases on GitHub](https://github.com/backstage/backstage/releases)

