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 --yesto 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.
Step 5: Enable AI-Powered Search
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:
- Go to Search tab
- Type: "how do we handle payment retries"
- 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:
- Search Test:
curl http://localhost:7007/api/search/query?term=payment+api
You should see: JSON response with service components matching "payment".
- Catalog Test:
Visit http://localhost:3000/catalog and verify your
payment-serviceappears with:
- ✅ Team ownership badge
- ✅ Dependency graph
- ✅ Links to dashboards
- 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
Option 1: Kubernetes (Recommended)
# 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:
| Service | Cost |
|---|---|
| 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
reposcope - Check
app-config.yamlhas 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.yamldescriptions - Increase
maxContextLengthin 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
- Backstage Discord - 15k+ platform engineers
- Official Plugins Repo - 100+ integrations
- Weekly Newsletter - New patterns and case studies
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