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:

In the next module, we'll explore Crossplane for infrastructure provisioning—the engine that powers self-service infrastructure requests. :::