Build a Platform Engineering Portal with Backstage & AI in 90 Minutes

Create a self-service developer portal using Backstage with AI-powered documentation and intelligent service discovery in under 2 hours.

Problem: Developers Waste Hours Finding Internal Tools

Your team spends 20% of their time hunting for API docs, service owners, and deployment procedures scattered across Confluence, Slack, and tribal knowledge.

You'll learn:

  • Deploy Backstage with AI-powered search in 90 minutes
  • Auto-generate service documentation from code
  • Build intelligent service catalogs with ownership tracking

Time: 90 min | Level: Intermediate


Why This Matters

Platform teams at companies like Spotify and Netflix use internal developer portals to reduce cognitive load. Without one, developers context-switch between 8+ tools just to deploy a service.

Common pain points:

  • "Who owns the payment service?" searches land in dead Slack threads
  • API documentation is 6 months outdated
  • New developers take 3 weeks to understand the stack

Prerequisites

Required:

  • Docker Desktop 4.28+ and Node.js 22.x
  • GitHub account (for OAuth)
  • OpenAI API key ($5 credit is enough)

Check versions:

docker --version  # Docker version 4.28.0
node --version    # v22.11.0

Solution

Step 1: Bootstrap Backstage

# Create app using official CLI
npx @backstage/create-app@latest

# Name it when prompted
? Enter a name for the app: platform-portal

cd platform-portal

Expected: Creates project structure with packages/app and packages/backend.

If it fails:

  • Error: "Node version incompatible": Upgrade to Node 22.x with nvm install 22
  • EACCES permission errors: Run with npx --yes to skip permission prompts

Step 2: Configure AI Documentation Plugin

Install the AI search plugin that indexes your GitHub repos:

# Add AI plugin dependencies
yarn workspace backend add @backstage-community/plugin-search-backend-module-ai
yarn workspace app add @backstage-community/plugin-ai-search

Create packages/backend/src/plugins/ai-search.ts:

import { createRouter } from '@backstage-community/plugin-search-backend-module-ai';
import { Router } from 'express';
import { PluginEnvironment } from '../types';

export default async function createPlugin(
  env: PluginEnvironment,
): Promise<Router> {
  return await createRouter({
    logger: env.logger,
    config: env.config,
    // AI indexes README files and code comments
    sources: ['github-repos'],
    aiProvider: {
      type: 'openai',
      model: 'gpt-4o-mini', // Cost-effective for embeddings
    },
  });
}

Why this works: The plugin generates embeddings from your documentation and enables semantic search instead of keyword matching.


Step 3: Connect GitHub Integration

Add GitHub OAuth to app-config.yaml:

integrations:
  github:
    - host: github.com
      token: ${GITHUB_TOKEN} # Set via environment variable

catalog:
  locations:
    # Auto-discover services with catalog-info.yaml
    - type: github-org
      target: https://github.com/YOUR_ORG
      rules:
        - allow: [Component, API, Resource]
  
  providers:
    githubOrg:
      id: production
      githubUrl: https://github.com
      orgs: ['YOUR_ORG']
      schedule:
        frequency: { hours: 1 }
        timeout: { minutes: 15 }

Set secrets:

# Create GitHub personal access token at github.com/settings/tokens
# Needs: repo, read:org, read:user

export GITHUB_TOKEN="ghp_your_token_here"
export OPENAI_API_KEY="sk-your_key_here"

Step 4: Create Service Catalog Template

Make a catalog file for one of your services at catalog-info.yaml in your repo root:

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: payment-service
  description: Handles payment processing and refunds
  annotations:
    # Links AI search will index
    github.com/project-slug: your-org/payment-service
    backstage.io/techdocs-ref: dir:.
  tags:
    - payments
    - python
    - critical
  links:
    - url: https://grafana.company.com/d/payments
      title: Dashboards
      icon: dashboard
spec:
  type: service
  lifecycle: production
  owner: payments-team
  system: billing
  dependsOn:
    - resource:postgres-payments-db
    - component:auth-service
  providesApis:
    - payment-api

Push to GitHub:

git add catalog-info.yaml
git commit -m "Add Backstage catalog"
git push

Backstage will discover this automatically within 1 hour, or manually refresh at /catalog-import.


Update packages/app/src/components/search/SearchPage.tsx:

import { AiSearchResultListItem } from '@backstage-community/plugin-ai-search';
import { SearchType } from '@backstage/plugin-search-react';

// Inside your SearchPage component
<SearchType.Tabs
  types={[
    {
      value: 'software-catalog',
      name: 'Services',
    },
    {
      value: 'ai-search',
      name: 'AI Search', // New semantic search tab
    },
    {
      value: 'techdocs',
      name: 'Docs',
    },
  ]}
/>

{/* Render AI results differently */}
{searchType === 'ai-search' && (
  <AiSearchResultListItem
    // Highlights relevant code snippets
    showCodeContext={true}
    maxContextLength={200}
  />
)}

Step 6: Launch Portal

# Start backend and frontend
yarn dev

You should see:

[0] webpack compiled successfully
[1] Backend started on port 7007

Visit: http://localhost:3000

Test AI search:

  1. Go to Search tab
  2. Type: "how do we handle payment retries"
  3. Should surface relevant code and docs from payment-service

Step 7: Add Service Ownership Tracking

Create a plugin to show team ownership in packages/app/src/components/catalog/EntityPage.tsx:

import { EntityOwnershipCard } from '@backstage/plugin-org';

// Add to service entity page
<EntityLayout.Route path="/ownership" title="Ownership">
  <Grid container spacing={3}>
    <Grid item xs={12} md={6}>
      <EntityOwnershipCard
        // Shows team members with links to profiles
        variant="gridItem"
      />
    </Grid>
    <Grid item xs={12} md={6}>
      <EntityDependencyOfCard variant="gridItem" />
    </Grid>
  </Grid>
</EntityLayout.Route>

Now developers can click any service and see who owns it, team Slack channel, and PagerDuty rotation.


Verification

Test the full workflow:

  1. Search Test:
curl http://localhost:7007/api/search/query?term=payment+api

You should see: JSON response with service components matching "payment".

  1. Catalog Test: Visit http://localhost:3000/catalog and verify your payment-service appears with:
  • ✅ Team ownership badge
  • ✅ Dependency graph
  • ✅ Links to dashboards
  1. AI Test: Search: "services that depend on postgres" Expected: AI returns services with dependsOn: resource:postgres-* even if keyword "postgres" isn't in description.

Production Deployment

# kubernetes/backstage-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backstage
spec:
  replicas: 2
  template:
    spec:
      containers:
      - name: backstage
        image: your-registry/backstage:latest
        env:
        - name: GITHUB_TOKEN
          valueFrom:
            secretKeyRef:
              name: backstage-secrets
              key: github-token
        - name: POSTGRES_HOST
          value: postgres-service
        ports:
        - containerPort: 7007

Build and push:

yarn build:all
docker build -t your-registry/backstage:latest .
docker push your-registry/backstage:latest
kubectl apply -f kubernetes/

Option 2: Railway/Render

Both support Dockerfile deployments with environment variables.

Dockerfile is included in Backstage template - no changes needed.


Cost Breakdown

Monthly estimates for 50-person engineering team:

ServiceCost
AI embeddings (OpenAI)~$15/mo
Hosting (2 replicas, 1GB each)$20-50/mo
PostgreSQL (managed)$15-30/mo
Total$50-95/mo

ROI: If saves each developer 2 hours/week finding docs = 400 hours/month = $20k+ saved at $50/hr.


What You Learned

  • Backstage automatically discovers services from GitHub
  • AI search understands context better than keywords (searches code semantics)
  • Catalog files (catalog-info.yaml) are the source of truth for service metadata

Limitations:

  • AI search requires code to have meaningful comments
  • GitHub org discovery needs broad token permissions
  • Initial catalog import takes 1-2 hours for large orgs

When NOT to use this:

  • Team smaller than 10 engineers (overhead exceeds benefit)
  • No standardized GitHub org structure
  • Security policy blocks AI service usage

Troubleshooting

"GitHub integration not finding repos":

  • Verify token has repo scope
  • Check app-config.yaml has correct org name
  • Look at backend logs: yarn workspace backend start --inspect

"AI search returns irrelevant results":

  • Ensure README files have clear descriptions
  • Add more detail to catalog-info.yaml descriptions
  • Increase maxContextLength in SearchPage config

"Catalog refresh stuck":

  • GitHub rate limits hit 5000/hour - wait or use GitHub App auth instead of PAT
  • Check Backstage logs for HTTP 403 errors

Community Resources


Tested on Backstage 1.24.0, Node.js 22.11, Docker Desktop 4.28, macOS Sonoma & Ubuntu 24.04 AI features require OpenAI API access as of Feb 2026