← Back to posts
comparison guide self-hosted · · 13 min read

Self-Hosted Developer Portal: Backstage vs Alternatives Complete Guide 2026

Complete guide to building a self-hosted developer portal in 2026. Compare Backstage, Port, and Compass. Includes Docker setup, plugin configuration, service catalog best practices, and cost analysis.

OS
Editorial Team

Every engineering organization eventually hits the same wall: services multiply, documentation scatters across wikis, nobody knows who owns what, and onboarding new developers takes weeks instead of days. A developer portal solves this by centralizing service discovery, documentation, tooling, and infrastructure access into a single, searchable interface.

The question is no longer whether to build one, but which platform to use and how to host it without handing your internal architecture to a third-party SaaS vendor.

This guide covers the leading open-source and self-hostable developer portal solutions in 2026, with practical deployment instructions for each.

Why Self-Host Your Developer Portal?

Developer portals contain your organization’s entire service topology — internal API endpoints, database connection strings, deployment pipelines, and ownership maps. Running this on someone else’s cloud introduces several risks:

Data sovereignty. Your service catalog reveals your architecture. Sending it to an external SaaS provider means your infrastructure topology lives on their servers, subject to their retention policies and breach risk.

Vendor lock-in. Once your teams depend on a proprietary portal, migrating away becomes a multi-month project. Open-source platforms keep your data in standard formats (YAML, JSON) that you own.

Cost at scale. SaaS developer portals typically charge per active user or per registered service. At 200+ engineers and 500+ microservices, annual costs easily exceed $50,000. Self-hosting costs a fraction — typically a single small VM or kubernetes cluster.

Deep customization. Open-source portals let you build plugins for your exact stack: custom CI/CD integrations, internal tool links, proprietary metrics dashboards, and authentication flows that SaaS products won’t support.

Air-gapped environments. Financial services, healthcare, and government teams often cannot send internal service metadata outside their network. Self-hosting is the only option.

The Landscape: Three Approaches

The developer portal market has settled into three distinct approaches:

FeatureBackstage (Spotify)PortCompass (Atlassian)
LicenseApache 2.0Commercial SaaSCommercial SaaS
Self-hostableYes, fullyNoNo
Primary focusService catalog + extensible plugin ecosystemDeveloper experience + self-serviceCode-centric service discovery
Setup complexityModerate to highLow (SaaS signup)Low (SaaS signup)
Customization depthUnlimited (React + TypeScript plugins)Limited to platform featuresLimited to Atlassian ecosystem
Community size40,000+ GitHub stars, CNCF projectGrowing SaaS customer baseAtlassian enterprise customers
Plugin ecosystem500+ official and community pluginsNative integrations onlyAtlassian marketplace apps
Best forTeams wanting full control and customizationTeams wanting quick setup with minimal opsAtlassian-heavy organizations
Estimated annual costInfrastructure only (~$500-$2,000/yr)$15-$40/user/month$10-$25/user/month

Backstage stands alone as the only genuinely self-hostable option with an open-source core. The other two require sending your data to vendor-managed infrastructure.

Getting Started with Backstage

Backstage is the CNCF-hosted developer portal originally built by Spotify. It provides a unified interface for managing your entire software catalog, including services, libraries, websites, and data pipelines.

Prerequisites

  • Node.js 20.x or 22.x LTS
  • Yarn 1.x (classic) or npm
  • PostgreSQLdockerfor production)
  • Docker and Docker Compose (optional, for containerized deployment)

Quick Start with npx

The fastest way to explore Backstage is generating a fresh app:

1
2
3
4
npx @backstage/create-app@latest
cd my-backstage-app
yarn install
yarn dev

This starts the frontend on port 3000 and the backend on port 7007. The development server includes hot-reload for plugin development.

Production Deployment with Docker Compose

For production, you need a persistent database and proper backend configuration. Here is a complete Docker Compose setup:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
version: '3.8'

services:
  backstage:
    build:
      context: .
      dockerfile: packages/backend/Dockerfile
    ports:
      - "7007:7007"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://backstage:backstage@postgres:5432/backstage
      - LOG_LEVEL=info
      # Authentication providers
      - AUTH_GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}
      - AUTH_GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET}
    depends_on:
      postgres:
        condition: service_healthy
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:7007/healthcheck"]
      interval: 30s
      timeout: 10s
      retries: 3

  postgres:
    image: postgres:16-alpine
    environment:
      - POSTGRES_USER=backstage
      - POSTGRES_PASSWORD=backstage
      - POSTGRES_DB=backstage
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U backstage"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  postgres_data:

Build and start the stack:

1
docker compose up -d --build

The portal will be available at http://localhost:7007.

Production Build with Helm for Kubernetes

For Kubernetes deployments, the official Helm chart handles everything:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
helm repo add backstage https://backstage.github.io/charts
helm repo update

helm install my-backstage backstage/backstage \
  --set backstage.extraEnvVars[0].name=DATABASE_URL \
  --set backstage.extraEnvVars[0].value=postgresql://backstage:backstage@my-postgres:5432/backstage \
  --set image.repository=myregistry/backstage \
  --set image.tag=latest \
  --namespace developer-portal \
  --create-namespace

Configuring the Software Catalog

The software catalog is Backstage’s core feature. It tracks every component in your organization using YAML descriptor files.

Entity Descriptor Structure

Each service, library, or system is defined in a catalog-info.yaml file at the repository root:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: payment-service
  description: Handles payment processing and refund workflows
  tags:
    - payments
    - stripe
    - production
  annotations:
    github.com/project-slug: myorg/payment-service
    circleci.com/project-slug: github/myorg/payment-service
    sonarqube.org/project-key: myorg_payment-service
spec:
  type: service
  lifecycle: production
  owner: team-payments
  system: checkout-platform
  dependsOn:
    - component:stripe-api
    - component:fraud-detection-service
  providesApis:
    - payment-api

Registering Catalog Locations

Tell Backstage where to find your catalog files by editing app-config.yaml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
catalog:
  locations:
    # Single target for a specific repository
    - type: url
      target: https://github.com/myorg/payment-service/blob/main/catalog-info.yaml
      rules:
        - allow: [Component, API, System, Resource]

    # Wildcard registration for all repositories
    - type: url
      target: https://github.com/myorg/*/blob/main/catalog-info.yaml
      rules:
        - allow: [Component, API, System, Resource]

    # Group and user imports from GitHub
    - type: url
      target: https://github.com/myorg/.github/blob/main/teams.yaml
      rules:
        - allow: [Group, User]

Organizing with Systems and Domains

For larger organizations, structure your catalog hierarchically:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# domains/ecommerce.yaml
apiVersion: backstage.io/v1alpha1
kind: Domain
metadata:
  name: ecommerce
  description: All services related to the online store
spec:
  owner: vp-engineering

# systems/checkout.yaml
apiVersion: backstage.io/v1alpha1
kind: System
metadata:
  name: checkout-platform
  description: Checkout flow, cart, and payment processing
spec:
  owner: team-checkout
  domain: ecommerce

This creates a navigable hierarchy: Domain → System → Component, making it easy for developers to understand how services relate to each other.

Essential Plugins

Backstage’s power comes from its plugin ecosystem. Here are the most useful plugins for a production deployment.

CI/CD Integration

Connect your pipelines so developers see build status directly in the portal:

1
2
3
4
5
6
7
8
# Install CircleCI plugin
yarn add --cwd packages/app @backstage/plugin-circleci

# Install GitHub Actions plugin  
yarn add --cwd packages/app @backstage/plugin-github-actions

# Install Jenkins plugin
yarn add --cwd packages/app @backstage/plugin-jenkins

Add to packages/app/src/App.tsx:

1
import { CircleCIPage, EntityCircleCIContent } from '@backstage/plugin-circleci';

Then register the route in your app configuration so each service page shows its recent builds.

Kubernetes Integration

The Kubernetes plugin shows live cluster resources, pod status, and deployment health for each registered service:

1
2
yarn add --cwd packages/app @backstage/plugin-kubernetes
yarn add --cwd packages/backend @backstage/plugin-kubernetes-backend

Configuration in app-config.yaml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
kubernetes:
  serviceLocatorMethod:
    type: 'multiTenant'
  clusterLocatorMethods:
    - type: 'config'
      clusters:
        - url: https://k8s-cluster.example.com:6443
          name: production
          authProvider: 'serviceAccount'
          skipTLSVerify: false
          skipMetricsLookup: false
          dashboardApp: 'portainer'

TechDocs (Documentation as Code)

TechDocs lets teams write documentation in Markdown alongside their code, which Backstage renders into a searchable documentation site:

1
2
yarn add --cwd packages/app @backstage/plugin-techdocs
yarn add --cwd packages/backend @backstage/plugin-techdocs-backend

Add a mkdocs.yml file to each repository:

1
2
3
4
5
6
site_name: Payment Service
nav:
  - Home: index.md
  - Architecture: architecture.md
  - API Reference: api-reference.md
  - Runbook: runbook.md

Backstage builds these into static HTML and serves them under each component’s TechDocs tab.

Scaffolder (Software Templates)

The scaffolder creates new services from standardized templates, enforcing your conventions automatically:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# templates/spring-boot-service/template.yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: spring-boot-service
  title: Spring Boot Microservice
  description: Create a new Spring Boot service with standard dependencies
spec:
  owner: team-platform
  type: service
  parameters:
    - title: Service Details
      required:
        - serviceName
        - description
      properties:
        serviceName:
          title: Service Name
          type: string
          ui:autofocus: true
        description:
          title: Description
          type: string
    - title: Repository Location
      properties:
        repoUrl:
          title: Repository URL
          type: string
          ui:field: RepoUrlPicker
          ui:options:
            allowedHosts:
              - github.com
  steps:
    - id: fetch-template
      name: Fetch Template
      action: fetch:template
      input:
        url: ./skeleton
        values:
          serviceName: ${{ parameters.serviceName }}
          description: ${{ parameters.description }}
          owner: ${{ user.entity.metadata.name }}
    - id: publish
      name: Publish
      action: publish:github
      input:
        allowedHosts: ['github.com']
        repoUrl: ${{ parameters.repoUrl }}
        defaultBranch: main
        protectDefaultBranch: true
    - id: register
      name: Register
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
        catalogInfoPath: '/catalog-info.yaml'

This template creates a new repository, populates it with a pre-configured Spring Boot project, adds the catalog descriptor, and registers the component automatically.

Scorecards and Quality Gates

Track service maturity across your organization:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apiVersion: backstage.io/v1alpha1
kind: ScoreCard
metadata:
  name: production-ready
spec:
  type: production-readiness
  scoring:
    strategy: aggregate
  checks:
    - name: Has Owner
      description: Every service must have a designated owner
      weight: 10
    - name: Has Documentation
      description: TechDocs must be present and non-empty
      weight: 15
    - name: CI Pipeline
      description: Automated build and test pipeline configured
      weight: 20
    - name: Health Endpoint
      description: /health endpoint returns 200
      weight: 15
    - name: Monitoring
      description: Grafana dashboard and PagerDuty integration
      weight: 20
    - name: Security Scan
      description: Trivy or Snyk scan passes with zero critical findings
      weight: 20

Services are scored from 0-100, and leadership gets a dashboard showing which services meet production standards.

Authentication and Access Control

Backstage supports multiple authentication providers out of the box:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
auth:
  environment: production
  providers:
    github:
      production:
        clientId: ${AUTH_GITHUB_CLIENT_ID}
        clientSecret: ${AUTH_GITHUB_CLIENT_SECRET}
    gitlab:
      production:
        clientId: ${AUTH_GITLAB_CLIENT_ID}
        clientSecret: ${AUTH_GITLAB_CLIENT_SECRET}
    oidc:
      production:
        metadataUrl: https://sso.example.com/.well-known/openid-configuration
        clientId: ${AUTH_OIDC_CLIENT_ID}
        clientSecret: ${AUTH_OIDC_CLIENT_SECRET}
        prompt: auto

For permission management, the permission framework lets you define fine-grained access policies:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// packages/backend/src/plugins/permissions.ts
import { PermissionPolicy } from '@backstage/plugin-permission-common';

export class MyPermissionPolicy implements PermissionPolicy {
  async handle(request, user) {
    // Allow all admins full access
    if (user.entity?.spec.profile?.email?.includes('@admin.example.com')) {
      return { result: 'ALLOW' };
    }

    // Restrict catalog edits to platform team
    if (request.permission.name === 'catalog.entity.refresh') {
      const isPlatformTeam = user.entity?.spec.memberships?.includes('team-platform');
      return {
        result: isPlatformTeam ? 'ALLOW' : 'DENY',
      };
    }

    return { result: 'ALLOW' };
  }
}

Monitoring and Maintenance

A self-hosted developer portal needs the same operational care as any production service.

Health Checks

Backstage exposes a health endpoint that Docker and Kubernetes can probe:

1
2
3
4
5
6
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:7007/healthcheck"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 60s

Database Maintenance

PostgreSQL requires periodic maintenance for the catalog database:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Vacuum and analyze for optimal query performance
psql -U backstage -d backstage -c "VACUUM ANALYZE;"

# Monitor table sizes
psql -U backstage -d backstage -c "
  SELECT schemaname, relname, 
    pg_size_pretty(pg_total_relation_size(relid)) as total_size
  FROM pg_catalog.pg_statio_user_tables 
  ORDER BY pg_total_relation_size(relid) DESC 
  LIMIT 10;
"

# Clean up old search data (run weekly via cron)
psql -U backstage -d backstage -c "
  DELETE FROM search.documents 
  WHERE updated_at < NOW() - INTERVAL '90 days';
"

Backup Strategy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
# backup-backstage.sh — run daily via cron

BACKUP_DIR="/var/backups/backstage"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)

# Database dump
pg_dump -U backstage -d backstage -F c -f "$BACKUP_DIR/backstage-db-$TIMESTAMP.dump"

# Compress application config
tar czf "$BACKUP_DIR/backstage-config-$TIMESTAMP.tar.gz" \
  app-config.yaml app-config.production.yaml \
  packages/app/src/components packages/backend/src/plugins

# Retain only last 30 days of backups
find "$BACKUP_DIR" -name "*.dump" -mtime +30 -delete
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +30 -delete

echo "Backup completed: $TIMESTAMP"

Alternative Approaches

Lightweight Portals for Small Teams

If Backstage feels like overkill for a team under 20 people, consider simpler approaches:

Gitea or GitLab wikis with standardized README conventions and a central index page can serve as a lightweight service directory.

Static site generators like Hugo or MkDocs with a custom theme can render your catalog from YAML files into a browsable website, deployed as a CI job.

Portainer or Rancher dashboards already provide service discovery and health monitoring for containerized workloads without additional infrastructure.

When Backstage Makes Sense

Backstage is the right choice when:

  • You have 50+ microservices across multiple teams
  • You need software templates to standardize new service creation
  • Multiple tool integrations (CI/CD, monitoring, security, cloud) need a unified view
  • You want custom plugins for internal tooling
  • Your organization requires data to stay within your infrastructure

Cost Comparison

For a 100-person engineering team managing 200 services:

Cost ItemBackstage (self-hosted)PortCompass
Software license$0 (Apache 2.0)$30,000/yr ($25/user/mo × 100)$18,000/yr ($15/user/mo × 100)
Infrastructure$1,200/yr (2 vCPU VM + Postgres)IncludedIncluded
Setup effort2-4 weeks1-2 days1-2 days
Ongoing maintenance4-8 hrs/monthMinimalMinimal
Year 1 total~$1,200 + engineering time~$30,000~$18,000
3-year total~$3,600 + engineering time~$90,000~$54,000

The infrastructure cost for self-hosting Backstage is typically a single small VM running PostgreSQL and the Backstage backend, or a small Kubernetes namespace if you already operate a cluster.

Conclusion

Self-hosting your developer portal gives you full ownership of your service catalog, unlimited customization through Backstage’s plugin architecture, and dramatically lower costs at scale. While the initial setup requires more effort than signing up for a SaaS alternative, the long-term benefits — data control, no per-user pricing, and the ability to build exactly the integrations your teams need — make it the right choice for organizations serious about developer experience.

Start with the Docker Compose setup to validate the concept, then move to Kubernetes with the Helm chart once your team confirms the value. The plugin ecosystem means you can grow from a simple service catalog into a full developer platform without switching tools.

For more self-hosted infrastructure guides, check out our comparisons of CI/CD platforms, container management dashboards, and monitoring stacks.

Frequently Asked Questions (FAQ)

Which one should I choose in 2026?

The best choice depends on your specific requirements:

  • For beginners: Start with the simplest option that covers your core use case
  • For production: Choose the solution with the most active community and documentation
  • For teams: Look for collaboration features and user management
  • For privacy: Prefer fully open-source, self-hosted options with no telemetry

Refer to the comparison table above for detailed feature breakdowns.

Can I migrate between these tools?

Most tools support data import/export. Always:

  1. Backup your current data
  2. Test the migration on a staging environment
  3. Check official migration guides in the documentation

Are there free versions available?

All tools in this guide offer free, open-source editions. Some also provide paid plans with additional features, priority support, or managed hosting.

How do I get started?

  1. Review the comparison table to identify your requirements
  2. Visit the official documentation (links provided above)
  3. Start with a Docker Compose setup for easy testing
  4. Join the community forums for troubleshooting
Advertise here