Developer Portals with Backstage
TechDocs & Plugins
3 min read
TechDocs brings documentation into Backstage using a "docs-as-code" approach, while plugins extend Backstage's functionality with integrations for your entire tool ecosystem.
TechDocs: Documentation as Code
TechDocs renders Markdown documentation directly in Backstage:
Traditional Docs:
┌─────────────┬─────────────┬─────────────┐
│ Confluence │ Notion │ Wiki │
└─────────────┴─────────────┴─────────────┘
"Docs are always out of date and hard to find"
TechDocs:
┌─────────────────────────────────────────┐
│ BACKSTAGE │
│ ┌───────────────────────────────────┐ │
│ │ Service: user-service │ │
│ │ [Overview] [API] [Docs] [CI/CD] │ │
│ │ │ │
│ │ 📄 Getting Started │ │
│ │ 📄 Architecture │ │
│ │ 📄 API Reference │ │
│ │ 📄 Runbook │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
"Docs live with code, always up to date"
Setting Up TechDocs
Add TechDocs to your service:
# In your repository root:
# mkdocs.yaml
site_name: User Service Documentation
nav:
- Home: index.md
- Getting Started: getting-started.md
- Architecture: architecture.md
- API Reference: api.md
- Runbook: runbook.md
plugins:
- techdocs-core
# Reference in catalog-info.yaml
# catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: user-service
annotations:
backstage.io/techdocs-ref: dir:. # Docs in same repo
# Or external: url:https://github.com/acme/user-service-docs
Create your documentation:
# docs/index.md
# User Service
Welcome to the User Service documentation.
## Quick Links
- [Getting Started](getting-started.md)
- [API Reference](api.md)
- [Runbook](runbook.md)
## Overview
The User Service handles authentication and user profile management.
## Architecture
```mermaid
graph LR
A[Client] --> B[API Gateway]
B --> C[User Service]
C --> D[(PostgreSQL)]
C --> E[Auth Provider]
Contact
- Owner: Backend Team
- Slack: #team-backend
- On-Call: PagerDuty
## TechDocs Configuration
Configure TechDocs in `app-config.yaml`:
```yaml
# app-config.yaml
techdocs:
builder: 'local' # 'local' or 'external'
generator:
runIn: 'local' # 'local' or 'docker'
publisher:
type: 'local' # 'local', 'awsS3', 'googleGcs', 'azureBlobStorage'
# For production with S3:
# type: 'awsS3'
# awsS3:
# bucketName: 'acme-techdocs'
# region: 'us-east-1'
Plugin Ecosystem
Backstage's power comes from plugins. Here are essential ones:
# Essential Backstage Plugins
plugins:
infrastructure:
- name: "@backstage/plugin-kubernetes"
purpose: "Show pod status, logs, events"
config: |
kubernetes:
serviceLocatorMethod:
type: multiTenant
clusterLocatorMethods:
- type: config
clusters:
- name: production
url: https://k8s.acme.com
authProvider: serviceAccount
- name: "@backstage/plugin-catalog-backend-module-aws"
purpose: "Discover AWS resources"
ci_cd:
- name: "@backstage/plugin-github-actions"
purpose: "Show GitHub Actions status"
- name: "@roadiehq/backstage-plugin-argo-cd"
purpose: "Show ArgoCD deployments"
monitoring:
- name: "@backstage/plugin-prometheus"
purpose: "Display Prometheus metrics"
- name: "@grafana/plugin-grafana"
purpose: "Embed Grafana dashboards"
incident_management:
- name: "@backstage/plugin-pagerduty"
purpose: "Show on-call, create incidents"
- name: "@backstage/plugin-opsgenie"
purpose: "OpsGenie integration"
security:
- name: "@backstage/plugin-security-insights"
purpose: "Show Dependabot alerts"
- name: "@roadiehq/backstage-plugin-snyk"
purpose: "Display Snyk vulnerabilities"
Installing Plugins
Add plugins to your Backstage app:
# Add a plugin (from backstage app root)
yarn add --cwd packages/app @backstage/plugin-kubernetes
# For backend plugins
yarn add --cwd packages/backend @backstage/plugin-kubernetes-backend
Register in your app:
// packages/app/src/App.tsx
import { KubernetesPage } from '@backstage/plugin-kubernetes';
const routes = (
<FlatRoutes>
{/* ... other routes */}
<Route path="/kubernetes" element={<KubernetesPage />} />
</FlatRoutes>
);
Kubernetes Plugin Example
Show real-time Kubernetes status:
# catalog-info.yaml - Enable Kubernetes plugin
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: user-service
annotations:
# Link to Kubernetes resources
backstage.io/kubernetes-id: user-service
backstage.io/kubernetes-namespace: production
backstage.io/kubernetes-label-selector: 'app=user-service'
# app-config.yaml - Kubernetes configuration
kubernetes:
serviceLocatorMethod:
type: 'multiTenant'
clusterLocatorMethods:
- type: 'config'
clusters:
- name: production
url: ${K8S_CLUSTER_URL}
authProvider: serviceAccount
serviceAccountToken: ${K8S_SERVICE_ACCOUNT_TOKEN}
skipTLSVerify: false
skipMetricsLookup: false
Building Custom Plugins
Create organization-specific plugins:
// plugins/acme-cost-insights/src/plugin.ts
import {
createPlugin,
createRoutableExtension,
} from '@backstage/core-plugin-api';
export const acmeCostInsightsPlugin = createPlugin({
id: 'acme-cost-insights',
routes: {
root: rootRouteRef,
},
});
export const AcmeCostInsightsPage = acmeCostInsightsPlugin.provide(
createRoutableExtension({
name: 'AcmeCostInsightsPage',
component: () =>
import('./components/CostInsightsPage').then(m => m.CostInsightsPage),
mountPoint: rootRouteRef,
}),
);
Plugin Marketplace
Find more plugins at:
- Backstage Plugin Marketplace: https://backstage.io/plugins
- Roadie Plugins: https://roadie.io/backstage/plugins/
- Community Plugins: https://github.com/backstage/community-plugins
In the next module, we'll explore Crossplane for infrastructure provisioning—the engine that powers self-service infrastructure requests. :::