Backstage (Spotify, CNCF) and Port are the two leading internal developer portals. Backstage is open-source and self-hosted. Port is SaaS with a free tier. Both provide software catalogs, self-service actions, and developer experience tooling — the difference is build vs buy.
Architecture
Backstage
Backstage is a React + Node.js application you host yourself:
- Software Catalog — YAML-based entity definitions (services, APIs, resources, teams)
- TechDocs — docs-as-code rendered from Markdown in repos
- Scaffolder — templates for creating new services, repos, infrastructure
- Plugin system — 200+ community plugins (Kubernetes, ArgoCD, PagerDuty, SonarQube)
- Search — unified search across catalog, docs, and plugins
Port
Port is a SaaS platform with an API-first architecture:
- Software Catalog — blueprint-based (define entity types, properties, relations)
- Self-Service Actions — forms that trigger GitHub Actions, GitLab CI, webhooks, Terraform
- Scorecards — measure engineering standards (test coverage, docs, ownership)
- Dashboards — customizable views per team/role
- Automations — event-driven workflows (e.g., auto-assign on-call when service created)
Feature comparison
| Feature | Backstage | Port |
|---|---|---|
| Deployment | Self-hosted | SaaS |
| License | Apache 2.0 | Commercial (free tier) |
| Software catalog | YAML entity definitions | Blueprints (schema-first) |
| Self-service | Scaffolder templates | Action forms (no-code/low-code) |
| Documentation | TechDocs (MkDocs) | External links |
| Scorecards | Via plugin (SoundCheck) | Built-in (native) |
| Search | Built-in (Lunr, Elasticsearch) | Built-in |
| RBAC | Plugin-based | Built-in (granular) |
| SSO | Configurable (OIDC, GitHub, etc.) | Built-in |
| Plugins | 200+ (community) | Integrations (built-in, fewer) |
| API | REST + GraphQL | REST |
| Kubernetes view | Kubernetes plugin | Kubernetes integration |
| Customization | Unlimited (React code) | Configuration-based (limited) |
| Maintenance | You manage upgrades, infra | Managed by Port |
| CNCF | Incubating | Not CNCF |
Catalog modeling
Backstage
Entities defined in YAML, stored in repos:
# catalog-info.yaml (in each service repo)
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: payment-service
description: Handles payment processing
annotations:
github.com/project-slug: myorg/payment-service
backstage.io/techdocs-ref: dir:.
spec:
type: service
lifecycle: production
owner: team-payments
dependsOn:
- resource:payments-db
providesApis:
- payment-apiPort
Blueprints defined in the Port UI or API:
{
"identifier": "microservice",
"title": "Microservice",
"schema": {
"properties": {
"language": { "type": "string", "enum": ["Go", "Python", "TypeScript"] },
"on_call": { "type": "string", "format": "user" },
"tier": { "type": "string", "enum": ["critical", "standard", "experimental"] },
"test_coverage": { "type": "number" }
}
},
"relations": {
"team": { "target": "team", "required": true },
"database": { "target": "database" }
}
}Port’s blueprint system is more flexible — you define custom entity types with typed properties, relations, and validations without writing code.
Self-service
Backstage Scaffolder
# template.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: create-service
title: Create a New Microservice
spec:
parameters:
- title: Service Details
properties:
name:
type: string
language:
type: string
enum: [go, python, typescript]
steps:
- id: fetch
action: fetch:template
input:
url: ./skeleton
values:
name: ${{ parameters.name }}
- id: publish
action: publish:github
input:
repoUrl: github.com?owner=myorg&repo=${{ parameters.name }}Port Self-Service Actions
Port actions are configured in the UI — no YAML files needed:
- Define input form fields (dropdowns, text, user selectors)
- Choose trigger: GitHub Action, GitLab CI, webhook, Terraform
- Map form inputs to trigger parameters
- Optionally add approval workflow
Port’s self-service is faster to set up (no code), but Backstage’s Scaffolder is more powerful for complex multi-step provisioning.
Scorecards
Port (native)
Port scorecards are built-in:
- Define rules: “All critical services must have on-call defined”
- Automatic scoring based on catalog data
- Dashboard shows compliance per team, per service tier
- Alerts when scores drop below threshold
Backstage (plugin)
Backstage requires the SoundCheck plugin (Spotify, commercial) or community alternatives. Not as mature as Port’s native implementation.
Cost
| Tier | Backstage | Port |
|---|---|---|
| Free | Free (self-hosted) | Free (up to 20 users) |
| Small team | Hosting cost (~$500-2K/mo) | ~$500/mo |
| Enterprise | Hosting + engineering time | Custom pricing |
| Hidden costs | Plugin development, upgrades, maintenance | None (SaaS) |
Backstage is “free” but requires engineering time to host, upgrade, build plugins, and maintain. Port is SaaS with predictable pricing but less customization.
Decision guide
Choose Backstage when:
- You want full control and customization — React plugins for any integration
- TechDocs (docs-as-code) is important to your workflow
- You have a platform team with frontend/backend engineers to build and maintain it
- You prefer open-source and vendor independence (CNCF)
- You need deep integrations via the 200+ plugin ecosystem
- Your organization is large enough to justify the maintenance investment
Choose Port when:
- You want a working portal fast — days not months
- Scorecards and compliance are a priority
- Your platform team is small and cannot maintain a self-hosted app
- You need self-service actions without writing Scaffolder templates
- RBAC and SSO must work out of the box
- You prefer SaaS over self-hosted infrastructure