Interne Entwicklerplattform IDP: Aufbau eines Unternehmens-Entwicklerportals mit Backstage 2026

DevOps

Interne Entwicklerplattform IDP: Aufbau eines Unternehmens-Entwicklerportals mit Backstage

Sie sind gerade einem Unternehmen mit 200 Microservices beigetreten. Am ersten Tag sagt Ihr Lead "deploye den order-service". Dann verbringen Sie 3 Stunden mit der Suche nach dem Git-Repo, 2 Stunden mit der Suche nach der CI/CD-Konfiguration, 1 Stunde mit der Suche nach der API-Dokumentation — der Tag ist vorbei, bevor Sie eine einzige Zeile Code geschrieben haben.

Das ist der Alltag ohne eine Interne Entwicklerplattform (IDP). Backstage, das von Spotify open-sourcte Entwicklerportal-Framework, wird zum De-facto-Standard für Platform Engineering. Dieser Artikel behandelt 5 Kernmuster, um ein Enterprise-IDP von Grund auf neu aufzubauen.

Kernkonzepte auf einen Blick

Konzept Beschreibung Analogie
IDP (Interne Entwicklerplattform) Einheitliches Self-Service-Portal für Entwickler Unternehmens-"App Store"
Backstage Open-Source-IDP-Framework von Spotify Das Kubernetes des IDP
Software Catalog Metadatenregister für alle Dienste "Gelbe Seiten" für Dienste
TechDocs Markdown-basiertes technisches Dokumentationssystem Engineering-grade Wiki
Software Template Standardisierte Projekterstellung mit einem Klick Webbasiertes Scaffolding-CLI
Plugin Backstage-Erweiterungsmodul Browser-Erweiterung

5 Hauptprobleme ohne IDP

  1. Schwierige Diensterkennung: Hunderte von Microservices, kann nicht finden, wem was gehört, wie deployed wird, wo überwacht wird
  2. Verstreute Dokumentation: Confluence, GitLab Wiki, Notion... nicht zu finden, nicht zu verstehen
  3. Langsames Projekt-Onboarding: Jedes Mal Gerüst von Grund auf neu aufbauen, CI/CD, Monitoring, Logging-Konfiguration ist repetitiv
  4. Fragmentierte Toolchain: Jenkins, ArgoCD, Grafana, Jira... ständiger Kontextwechsel killt die Produktivität
  5. Compliance-Audit-Albtraum: Wer hat was geändert, wer hat Zugriff, der Dienstabhängigkeitsgraph ist ein Chaos

Muster 1: Backstage-Architektur und Installation

Backstage verwendet eine plugin-basierte Monolith-Architektur. Der Kern besteht aus vier Modulen: Catalog + TechDocs + Scaffold + Search, erweitert durch Plugins.

# Backstage-Installation und Initialisierung
# Umgebung: Node.js 20.x+ / yarn 1.22+ / Git 2.40+
# Abhängigkeiten: @backstage/cli 1.32+ / @backstage/core 1.30+

# 1. Backstage-App erstellen
npx @backstage/create-app@latest --name my-idp
cd my-idp

# 2. Projektstruktur
# my-idp/
# ├── app/                    # Frontend React-App
# │   ├── src/
# │   │   ├── components/     # Benutzerdefinierte Komponenten
# │   │   ├── plugins/        # Frontend-Plugins
# │   │   └── App.tsx         # App-Einstiegspunkt
# ├── packages/
# │   ├── backend/            # Backend Node.js-Dienst
# │   └── app/                # Frontend-Build-Paket
# ├── plugins/                # Verzeichnis für benutzerdefinierte Plugins
# ├── catalog-info.yaml       # Katalog-Entitätsdefinitionen
# └── app-config.yaml         # App-Konfiguration

# 3. Dev-Server starten
yarn dev

# 4. Produktionsversion erstellen
yarn build:backend
# app-config.yaml: Backstage-Hauptkonfiguration
# Umgebung: Backstage 1.32+
app:
  title: Unternehmens-Entwicklerportal
  baseUrl: http://localhost:3000

organization:
  name: MyCompany

backend:
  baseUrl: http://localhost:7007
  listen:
    port: 7007
  csp:
    connect-src: ["'self'", 'http:', 'https:']
  cors:
    origin: http://localhost:3000
    methods: [GET, HEAD, PATCH, POST, PUT, DELETE]
    credentials: true
  database:
    client: pg
    connection:
      host: ${POSTGRES_HOST}
      port: ${POSTGRES_PORT:-5432}
      user: ${POSTGRES_USER}
      password: ${POSTGRES_PASSWORD}
      database: backstage_plugin

integrations:
  github:
    - host: github.com
      token: ${GITHUB_TOKEN}
  gitlab:
    - host: gitlab.mycompany.com
      token: ${GITLAB_TOKEN}

auth:
  environment: development
  providers:
    github:
      development:
        clientId: ${GITHUB_CLIENT_ID}
        clientSecret: ${GITHUB_CLIENT_SECRET}
    oidc:
      production:
        metadataUrl: ${OIDC_METADATA_URL}
        clientId: ${OIDC_CLIENT_ID}
        clientSecret: ${OIDC_CLIENT_SECRET}

catalog:
  rules:
    - allow: [Component, System, API, Resource, Location, Template]
  locations:
    - type: url
      target: https://github.com/mycompany/catalog/blob/main/catalog-info.yaml
    - type: url
      target: https://gitlab.mycompany.com/platform/catalog/-/blob/main/all.yaml

techdocs:
  builder: 'local'
  generator:
    runIn: 'docker'
  publisher:
    type: 'local'

Muster 2: Software Catalog

Der Catalog ist das Herzstück von Backstage — das Metadatenregister für alle Dienste. Definieren Sie Entitäten über YAML-Deskriptordateien mit automatischer Diensterkennung und -indizierung.

# catalog-info.yaml: Dienstkomponenten-Registrierung
# Umgebung: Backstage 1.32+ / Catalog API v1
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: order-service
  description: Kern-Bestelldienst zur Verarbeitung von Bestellerstellung, Zahlung und Fulfillment
  annotations:
    backstage.io/source-location: url:https://github.com/mycompany/order-service
    backstage.io/techdocs-ref: dir:.
    github.com/project-slug: mycompany/order-service
    gitlab.com/project-id: "12345"
    jira.com/project-key: ORD
    grafana/dashboard-tag: order-service
    prometheus.io/alert: order-service-alerts
  tags:
    - java
    - spring-boot
    - microservice
    - tier-1
  links:
    - url: https://grafana.mycompany.com/d/order-service
      title: Grafana-Monitoring
      icon: dashboard
    - url: https://argocd.mycompany.com/applications/order-service
      title: ArgoCD-Bereitstellung
      icon: cloud
    - url: https://order-service.mycompany.com/swagger
      title: API-Dokumentation
      icon: api
spec:
  type: service
  lifecycle: production
  owner: team-order
  system: order-system
  dependsOn:
    - component:payment-service
    - component:inventory-service
    - resource:order-db
  providesApis:
    - order-api
  consumesApis:
    - payment-api
    - inventory-api
---
apiVersion: backstage.io/v1alpha1
kind: API
metadata:
  name: order-api
  description: Bestelldienst REST-API
spec:
  type: openapi
  lifecycle: production
  owner: team-order
  system: order-system
  definition:
    $text: https://raw.githubusercontent.com/mycompany/order-service/main/openapi.yaml
---
apiVersion: backstage.io/v1alpha1
kind: System
metadata:
  name: order-system
  description: Bestellsystem inklusive Bestell-, Zahlungs- und Inventardiensten
spec:
  owner: team-order
  domain: commerce
---
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
  name: team-order
  description: Bestellteam
spec:
  type: team
  parent: dept-commerce
  children: []
  members:
    - john.doe
    - jane.smith
    - bob.wilson
// TypeScript: Benutzerdefinierter Katalog-Entitätsprozessor
// Umgebung: Node.js 20.x+ / @backstage/plugin-catalog-node 1.12+
import {
  CatalogProcessor,
  CatalogProcessorEmit,
  processingResult,
} from '@backstage/plugin-catalog-node';
import { LocationSpec, Location } from '@backstage/plugin-catalog-common';
import { Entity } from '@backstage/catalog-model';

export class CustomServiceProcessor implements CatalogProcessor {
  getProcessorName(): string {
    return 'CustomServiceProcessor';
  }

  async validateLocationKind(location: Location): Promise<boolean> {
    return location.type === 'url';
  }

  async preProcessEntity(
    entity: Entity,
    location: LocationSpec,
    emit: CatalogProcessorEmit,
  ): Promise<Entity> {
    // Compliance-Tags automatisch für alle Produktionsdienste hinzufügen
    if (
      entity.kind === 'Component' &&
      entity.spec?.lifecycle === 'production'
    ) {
      const annotations = entity.metadata.annotations ?? {};
      annotations['compliance.mycompany.com/scan-required'] = 'true';
      annotations['compliance.mycompany.com/last-scan'] = new Date().toISOString();
      entity.metadata.annotations = annotations;
    }

    // Dienstabhängigkeitsbeziehungen automatisch generieren
    if (entity.kind === 'Component' && entity.spec?.dependsOn) {
      const deps = entity.spec.dependsOn as string[];
      for (const dep of deps) {
        emit(
          processingResult.relation({
            source: {
              name: entity.metadata.name,
              namespace: entity.metadata.namespace ?? 'default',
              kind: entity.kind,
            },
            target: {
              name: dep.replace('component:', ''),
              namespace: 'default',
              kind: 'Component',
            },
            type: 'dependsOn',
          }),
        );
      }
    }

    return entity;
  }
}

Muster 3: TechDocs

TechDocs basiert auf MkDocs + Markdown und folgt der Docs-as-Code-Philosophie — Dokumentation lebt neben Code im gleichen Repository-Lebenszyklus.

# mkdocs.yaml: TechDocs-Konfiguration
# Umgebung: MkDocs 1.6+ / mkdocs-material 9.5+
site_name: Technische Dokumentation des Bestelldienstes
site_description: Vollständige technische Dokumentation für den Bestelldienst

nav:
  - Startseite: index.md
  - Erste Schritte: getting-started.md
  - Architektur:
    - Systemübersicht: architecture/overview.md
    - Datenmodell: architecture/data-model.md
    - API-Design: architecture/api-design.md
  - Betrieb:
    - Bereitstellungsleitfaden: ops/deployment.md
    - Monitoring & Alerts: ops/monitoring.md
    - Fehlerbehebung: ops/troubleshooting.md
  - Entwicklung:
    - Codierungsstandards: dev/coding-standards.md
    - Git-Workflow: dev/git-workflow.md
    - Teststrategie: dev/testing.md

plugins:
  - techdocs-core:
      add_mermaid: true
      add_plantuml: true

markdown_extensions:
  - admonition
  - codehilite
  - footnotes
  - meta
  - toc:
      permalink: true
  - pymdownx.superfences:
      custom_fences:
        - name: mermaid
          class: mermaid
          format: !!python/name:pymdownx.superfences.fence_code_format
<!-- docs/index.md: TechDocs-Startseite -->
# Bestelldienst

## Übersicht

Der Bestelldienst ist der Kern-Microservice des Bestellsystems und verarbeitet Bestellerstellung, Zahlung und Fulfillment.

## Schnelllinks

| Ressource | Link |
|----------|------|
| Quellcode | [GitHub](https://github.com/mycompany/order-service) |
| CI/CD | [Jenkins](https://jenkins.mycompany.com/job/order-service) |
| Monitoring | [Grafana](https://grafana.mycompany.com/d/order-service) |
| API-Dokumentation | [Swagger](https://order-service.mycompany.com/swagger) |

## Architektur

```mermaid
graph TB
    A[API Gateway] --> B[Order Service]
    B --> C[Payment Service]
    B --> D[Inventory Service]
    B --> E[(Order DB)]
    B --> F[Redis Cache]
    B --> G[Kafka]

Kennzahlen

Metrik Ziel Aktuell
P99-Latenz <200ms 150ms
Verfügbarkeit 99.95% 99.97%
Fehlerrate <0.1% 0.05%

## Muster 4: Plugin-Entwicklung

Das Plugin-System von Backstage ist sein Kernwettbewerbsvorteil. Benutzerdefinierte Plugins ermöglichen es dem IDP, sich wirklich an Ihre Unternehmens-Toolchain anzupassen.

```typescript
// TypeScript: Benutzerdefiniertes Backstage-Plugin - Dienst-Gesundheitsprüfungs-Panel
// Umgebung: Node.js 20.x+ / @backstage/core-plugin-api 1.9+ / React 18.x

// --- Frontend-Plugin ---

// plugins/service-health/src/plugin.ts
import {
  createPlugin,
  createRoutableExtension,
} from '@backstage/core-plugin-api';

export const serviceHealthPlugin = createPlugin({
  id: 'service-health',
  routes: {
    root: rootRouteRef,
  },
});

export const ServiceHealthPage = serviceHealthPlugin.provide(
  createRoutableExtension({
    name: 'ServiceHealthPage',
    component: () =>
      import('./components/ServiceHealthPanel').then(
        (m) => m.ServiceHealthPanel,
      ),
    mountPoint: rootRouteRef,
  }),
);

// plugins/service-health/src/components/ServiceHealthPanel.tsx
import React from 'react';
import { useEntity } from '@backstage/plugin-catalog-react';
import {
  Header,
  Page,
  Content,
  InfoCard,
  Progress,
  StatusOK,
  StatusError,
  StatusWarning,
} from '@backstage/core-components';
import { useApi } from '@backstage/core-plugin-api';
import { serviceHealthApiRef } from '../api';

interface HealthCheckResult {
  name: string;
  status: 'healthy' | 'degraded' | 'down';
  latency_ms: number;
  last_check: string;
  message: string;
}

export const ServiceHealthPanel: React.FC = () => {
  const { entity } = useEntity();
  const healthApi = useApi(serviceHealthApiRef);
  const [healthData, setHealthData] = React.useState<HealthCheckResult[]>([]);
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    const serviceName = entity.metadata.name;
    healthApi
      .getServiceHealth(serviceName)
      .then((data) => {
        setHealthData(data.checks);
        setLoading(false);
      })
      .catch(() => setLoading(false));
  }, [entity, healthApi]);

  if (loading) return <Progress />;

  const StatusIcon = ({ status }: { status: string }) => {
    switch (status) {
      case 'healthy':
        return <StatusOK />;
      case 'degraded':
        return <StatusWarning />;
      case 'down':
        return <StatusError />;
      default:
        return null;
    }
  };

  return (
    <Page themeId="tool">
      <Header title={`Gesundheitsprüfung von ${entity.metadata.name}`} />
      <Content>
        {healthData.map((check) => (
          <InfoCard key={check.name} title={check.name}>
            <StatusIcon status={check.status} />
            <p>Status: {check.status}</p>
            <p>Latenz: {check.latency_ms}ms</p>
            <p>Letzte Prüfung: {check.last_check}</p>
            <p>Nachricht: {check.message}</p>
          </InfoCard>
        ))}
      </Content>
    </Page>
  );
};
// TypeScript: Backstage-Backend-Plugin
// Umgebung: Node.js 20.x+ / @backstage/backend-common 0.25+

// plugins/service-health-backend/src/plugin.ts
import {
  createBackendPlugin,
  coreServices,
} from '@backstage/backend-plugin-api';
import { createRouter } from './router';

export const serviceHealthBackendPlugin = createBackendPlugin({
  pluginId: 'service-health',
  register(env) {
    env.registerInit({
      deps: {
        logger: coreServices.logger,
        config: coreServices.rootConfig,
        httpRouter: coreServices.httpRouter,
        discovery: coreServices.discovery,
      },
      async init({ logger, config, httpRouter, discovery }) {
        const router = await createRouter({
          logger,
          config,
          discovery,
        });

        httpRouter.use(router);
        httpRouter.addAuthPolicy({
          path: '/health',
          allow: 'unauthenticated',
        });

        logger.info('Dienst-Gesundheits-Plugin initialisiert');
      },
    });
  },
});

// plugins/service-health-backend/src/router.ts
import express, { Router } from 'express';
import { LoggerService, RootConfigService, DiscoveryService } from '@backstage/backend-plugin-api';

interface RouterOptions {
  logger: LoggerService;
  config: RootConfigService;
  discovery: DiscoveryService;
}

export async function createRouter(
  options: RouterOptions,
): Promise<Router> {
  const { logger, config } = options;
  const router = Router();

  router.get('/health', (_req, res) => {
    res.json({ status: 'ok' });
  });

  router.get('/services/:serviceName/health', async (req, res) => {
    const { serviceName } = req.params;

    try {
      const healthChecks = await fetchServiceHealth(config, serviceName);
      res.json({
        service: serviceName,
        checks: healthChecks,
        timestamp: new Date().toISOString(),
      });
    } catch (error) {
      logger.error(`Fehler beim Abrufen des Gesundheitsstatus von ${serviceName}: ${error}`);
      res.status(500).json({ error: 'Health check failed' });
    }
  });

  return router;
}

async function fetchServiceHealth(
  config: RootConfigService,
  serviceName: string,
): Promise<Array<{
  name: string;
  status: 'healthy' | 'degraded' | 'down';
  latency_ms: number;
  last_check: string;
  message: string;
}>> {
  const prometheusUrl = config.getString('monitoring.prometheus.url');

  return [
    {
      name: 'HTTP Endpoint',
      status: 'healthy',
      latency_ms: 45,
      last_check: new Date().toISOString(),
      message: 'Alle Endpunkte antworten normal',
    },
    {
      name: 'Database Connection',
      status: 'healthy',
      latency_ms: 12,
      last_check: new Date().toISOString(),
      message: 'Verbindungspool gesund',
    },
    {
      name: 'Kafka Consumer',
      status: 'degraded',
      latency_ms: 250,
      last_check: new Date().toISOString(),
      message: 'Consumer-Lag überschreitet Schwellenwert',
    },
  ];
}

Muster 5: Produktionsgrade IDP-Bereitstellung

# docker-compose.yml: Backstage-Produktionsbereitstellung
# Umgebung: Docker Compose v2.30+ / PostgreSQL 16
version: "3.8"

services:
  backstage:
    image: mycompany/backstage:1.32.0
    ports:
      - "7007:7007"
    environment:
      APP_CONFIG_app_baseUrl: https://idp.mycompany.com
      APP_CONFIG_backend_baseUrl: https://idp.mycompany.com
      APP_CONFIG_backend_database_client: pg
      APP_CONFIG_backend_database_connection_host: postgres
      APP_CONFIG_backend_database_connection_port: "5432"
      APP_CONFIG_backend_database_connection_user: ${POSTGRES_USER}
      APP_CONFIG_backend_database_connection_password: ${POSTGRES_PASSWORD}
      POSTGRES_HOST: postgres
      POSTGRES_PORT: "5432"
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      GITHUB_TOKEN: ${GITHUB_TOKEN}
      GITLAB_TOKEN: ${GITLAB_TOKEN}
    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_DB: backstage_plugin
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

volumes:
  postgres_data:
# kubernetes/backstage-deployment.yaml: K8s-Produktionsbereitstellung
# Umgebung: Kubernetes 1.30+ / Helm 3.15+
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backstage
  namespace: idp
  labels:
    app: backstage
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backstage
  template:
    metadata:
      labels:
        app: backstage
    spec:
      containers:
        - name: backstage
          image: mycompany/backstage:1.32.0
          ports:
            - containerPort: 7007
          env:
            - name: APP_CONFIG_app_baseUrl
              value: "https://idp.mycompany.com"
            - name: APP_CONFIG_backend_baseUrl
              value: "https://idp.mycompany.com"
            - name: APP_CONFIG_backend_database_client
              value: "pg"
            - name: APP_CONFIG_backend_database_connection_host
              valueFrom:
                secretKeyRef:
                  name: backstage-db-secret
                  key: host
            - name: APP_CONFIG_backend_database_connection_password
              valueFrom:
                secretKeyRef:
                  name: backstage-db-secret
                  key: password
          resources:
            requests:
              cpu: "500m"
              memory: "512Mi"
            limits:
              cpu: "2000m"
              memory: "2Gi"
          livenessProbe:
            httpGet:
              path: /healthcheck
              port: 7007
            initialDelaySeconds: 30
            periodSeconds: 30
          readinessProbe:
            httpGet:
              path: /healthcheck
              port: 7007
            initialDelaySeconds: 10
            periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: backstage
  namespace: idp
spec:
  selector:
    app: backstage
  ports:
    - port: 80
      targetPort: 7007
  type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: backstage
  namespace: idp
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  tls:
    - hosts:
        - idp.mycompany.com
      secretName: backstage-tls
  rules:
    - host: idp.mycompany.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: backstage
                port:
                  number: 80
# Helm-Bereitstellung (empfohlen)
# Umgebung: Helm 3.15+ / Kubernetes 1.30+

# 1. Backstage-Helm-Repo hinzufügen
helm repo add backstage https://backstage.github.io/charts
helm repo update

# 2. Werte-Override-Datei erstellen
cat > backstage-values.yaml << 'EOF'
backstage:
  image:
    repository: mycompany/backstage
    tag: 1.32.0
  extraAppConfig:
    - configMapRef: backstage-config
  extraEnvVarsSecrets:
    - backstage-secrets

ingress:
  enabled: true
  host: idp.mycompany.com
  tls:
    enabled: true
    secretName: backstage-tls

postgresql:
  enabled: true
  primary:
    persistence:
      size: 20Gi
  auth:
    existingSecret: backstage-db-secret
EOF

# 3. Bereitstellen
helm install backstage backstage/backstage \
  -n idp \
  --create-namespace \
  -f backstage-values.yaml

# 4. Überprüfen
kubectl get pods -n idp
kubectl port-forward svc/backstage 7007:80 -n idp

Fallenleitfaden: 5 Häufige Fallen

Falle 1: Katalog-Entität wird nach Registrierung nicht angezeigt

❌ Falsch: Nur catalog-info.yaml lokal erstellen ohne Registrierung eines Location
✅ Richtig: Locations in app-config.yaml hinzufügen oder Location in der UI registrieren

Falle 2: TechDocs-Build-Fehler

❌ Falsch: MkDocs direkt auf dem Host ausführen, fehlende Abhängigkeiten verursachen Build-Fehler
✅ Richtig: techdocs.generator.runIn: 'docker' für containerisierte Builds konfigurieren

Falle 3: Unzureichende GitHub/GitLab-Integrationstoken-Berechtigungen

# ❌ Falsch: Token hat nur repo:read-Berechtigung
# ✅ Richtig: Token benötigt diese Berechtigungen
# GitHub: repo, read:org, read:user
# GitLab: api, read_repository, read_api

Falle 4: Plugin-Hot-Reload funktioniert nicht

# ❌ Falsch: Dev-Server nach Änderung des Plugin-Codes nicht neu starten
# ✅ Richtig: Sicherstellen, dass yarn dev läuft, Backstage unterstützt HMR-Hot-Reload
# Falls HMR nicht funktioniert, Cache leeren und neu starten:
yarn cache clean
yarn dev

Falle 5: Erschöpfung des Produktionsdatenbank-Verbindungspools

# app-config.yaml: Verbindungspool konfigurieren
backend:
  database:
    client: pg
    connection:
      host: postgres
      port: 5432
      user: ${POSTGRES_USER}
      password: ${POSTGRES_PASSWORD}
      pool:
        min: 5
        max: 20
        idleTimeoutMillis: 30000
        connectionTimeoutMillis: 5000

Fehlerbehebungstabelle

Fehlermeldung Ursache Lösung
Failed to fetch catalog entities Ungültiges GitHub/GitLab-Token oder unzureichende Berechtigungen Token-Berechtigungen prüfen, repo und read:org sicherstellen
ECONNREFUSED 127.0.0.1:5432 PostgreSQL läuft nicht oder falsche Verbindungskonfiguration Datenbankdienst und Verbindungsparameter prüfen
TechDocs generation failed MkDocs-Build-Fehler, fehlende Abhängigkeiten runIn: 'docker' setzen oder fehlende Python-Pakete installieren
Plugin not found: xxx Plugin nicht im Backend registriert Plugin in packages/backend/src/index.ts importieren und registrieren
Unauthorized: Invalid token OIDC/OAuth-Fehlkonfiguration clientId/clientSecret und Callback-URL prüfen
CORS error Frontend/Backend-Domain-Nichtübereinstimmung backend.cors.origin konfigurieren, um mit der Frontend-URL übereinzustimmen
Location not found catalog-info.yaml-URL nicht erreichbar URL-Erreichbarkeit und Token-Berechtigungen verifizieren
Out of memory Node.js-Heap-Speicher unzureichend NODE_OPTIONS=--max-old-space-size=4096 setzen
Schema validation failed catalog-info.yaml-Formatfehler Mit backstage-cli catalog-validate validieren
Template execution timeout Software-Template-Ausführungs-Timeout HTTP-Anfragen im Template prüfen, Timeout-Konfiguration erhöhen

Erweiterte Optimierung: 5 Produktionsgrade Tipps

Tipp 1: Katalog-Auto-Discovery

// TypeScript: GitLab-Auto-Discovery-Prozessor
// Umgebung: @backstage/plugin-catalog-backend-module-gitlab 0.8+
import { GitLabDiscoveryProcessor } from '@backstage/plugin-catalog-backend-module-gitlab';

const config = {
  catalog: {
    processors: [
      GitLabDiscoveryProcessor.fromConfig(env.config, {
        logger: env.logger,
        schedule: {
          frequency: { minutes: 30 },
          timeout: { minutes: 3 },
          initialDelay: { seconds: 15 },
        },
      }),
    ],
  },
};

Tipp 2: RBAC-Berechtigungskontrolle

# app-config.yaml: RBAC-Konfiguration
# Umgebung: @backstage/plugin-permission-backend 0.6+
permission:
  enabled: true
  rbac:
    policies:
      - name: 'role:default/developer'
        permissions:
          - 'catalog.entity.read'
          - 'techdocs.read'
          - 'scaffolder.template.execute'
      - name: 'role:default/admin'
        permissions:
          - 'catalog.entity.create'
          - 'catalog.entity.delete'
          - 'catalog.entity.update'
          - 'techdocs.write'
          - 'scaffolder.template.create'
    policyFile: ./rbac-policy.csv

Tipp 3: Suchverbesserung

# app-config.yaml: Suchmaschinenkonfiguration
# Umgebung: @backstage/plugin-search-backend-module-elasticsearch 1.6+
search:
  collators:
    techdocs:
      schedule:
        frequency: { minutes: 10 }
        timeout: { minutes: 5 }
        initialDelay: { seconds: 30 }
  elasticsearch:
    url: ${ELASTICSEARCH_URL}
    auth:
      username: ${ELASTICSEARCH_USER}
      password: ${ELASTICSEARCH_PASSWORD}
    indexPrefix: backstage_search_

Tipp 4: Multi-Cluster-ArgoCD-Integration

# app-config.yaml: ArgoCD-Multi-Cluster-Integration
# Umgebung: @backstage/plugin-argocd 2.5+
argocd:
  appLocatorMethods:
    - type: 'config'
      instances:
        - name: production
          url: https://argocd.mycompany.com
          token: ${ARGOCD_TOKEN_PROD}
        - name: staging
          url: https://argocd-staging.mycompany.com
          token: ${ARGOCD_TOKEN_STAGING}

Tipp 5: Golden-Path-Templates

# templates/java-spring-boot/template.yaml: Golden-Path-Scaffolding-Template
# Umgebung: @backstage/plugin-scaffolder 1.25+
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: java-spring-boot-service
  title: Java Spring Boot Microservice-Template
  description: Einen standardisierten Spring Boot-Microservice mit CI/CD, Monitoring und Dokumentation erstellen
spec:
  owner: platform-team
  type: service
  parameters:
    - title: Dienstinformationen
      required:
        - name
        - owner
      properties:
        name:
          title: Dienstname
          type: string
          pattern: '^[a-z][a-z0-9-]*$'
          description: Nur Kleinbuchstaben, Zahlen und Bindestriche
        owner:
          title: Besitzerteam
          type: string
          ui:field: OwnerPicker
          ui:options:
            allowedKinds:
              - Group
        description:
          title: Dienstbeschreibung
          type: string
          description: Einzeilige Beschreibung der Dienstfunktionalität
        database:
          title: Datenbanktyp
          type: string
          default: postgresql
          enum:
            - postgresql
            - mysql
            - mongodb
            - none
    - title: CI/CD-Konfiguration
      properties:
        ciProvider:
          title: CI-Tool
          type: string
          default: github-actions
          enum:
            - github-actions
            - gitlab-ci
            - jenkins
        deployTarget:
          title: Bereitstellungsziel
          type: string
          default: kubernetes
          enum:
            - kubernetes
            - ecs
            - cloud-run

  steps:
    - id: fetch-template
      name: Template-Code abrufen
      action: fetch:template
      input:
        url: ./skeleton
        values:
          name: ${{ parameters.name }}
          owner: ${{ parameters.owner }}
          description: ${{ parameters.description }}
          database: ${{ parameters.database }}
          ciProvider: ${{ parameters.ciProvider }}
          deployTarget: ${{ parameters.deployTarget }}

    - id: publish
      name: Im Git-Repository veröffentlichen
      action: publish:github
      input:
        allowedHosts: ['github.com']
        description: ${{ parameters.description }}
        repoUrl: github.com?owner=mycompany&repo=${{ parameters.name }}
        defaultBranch: main
        protectDefaultBranch: true

    - id: register
      name: Im Katalog registrieren
      action: catalog:register
      input:
        repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
        catalogInfoPath: '/catalog-info.yaml'

  output:
    links:
      - title: Repository-URL
        url: ${{ steps.publish.output.remoteUrl }}
      - title: Im Katalog öffnen
        url: https://idp.mycompany.com/catalog/default/component/${{ parameters.name }}

IDP-Plattformvergleich

Dimension Backstage Port Humanitec
Open Source ✅ Apache 2.0 ❌ Kommerzielles SaaS ❌ Kommerzielles SaaS
Self-Hosted ✅ Volle Kontrolle ❌ Cloud-gehostet ❌ Cloud-gehostet
Plugin-Ökosystem 200+ Community-Plugins Integrierte Integrationen Integrierte Integrationen
Lernkurve Mittel-Hoch (TypeScript) Niedrig (visuelle Konfiguration) Mittel (Platform Engineering)
Anpassbarkeit Sehr hoch (Code-Ebene) Mittel (API-Erweiterung) Mittel (Score-Definition)
Software Catalog ✅ Kernfunktion ✅ Kernfunktion ✅ Kernfunktion
TechDocs ✅ Native Unterstützung ❌ Integration erforderlich ❌ Integration erforderlich
Kosten Engineering-Aufwand $50-200/Benutzer/Monat $100-500/Benutzer/Monat
Skalierungsfit 50-5000+ Entwickler 10-500 Entwickler 100-5000+ Entwickler
Bekannte Kunden Spotify, American Airlines Lyft, Palo Alto BMW, Qonto

Fazit

Backstage ist die optimale Wahl für den Aufbau einer Internen Entwicklerplattform im Jahr 2026 — Open Source, self-hosted, mit einem reichen Plugin-Ökosystem. Aber beachten Sie:

  • Kleine Teams (<50): Zuerst Port oder ähnliche SaaS-Lösungen in Betracht ziehen; Backstage-Wartung möglicherweise nicht kosteneffizient
  • Mittlere Teams (50-500): Backstage ist die beste Wahl, 2-3 Ingenieure für Wartung
  • Große Teams (500+): Backstage + Golden-Path-Templates + Auto-Discovery für ein vollständiges Platform-Engineering-System

Erfolgsfaktoren: Starten Sie mit Catalog und TechDocs, fügen Sie schrittweise Plugins hinzu, bauen Sie dann Golden-Path-Templates. Versuchen Sie nicht, den Ozean zum Kochen zu bringen.

Empfohlene Online-Tools

Probiere diese browser-lokalen Tools aus — keine Registrierung erforderlich →

#内部开发者平台#Backstage#IDP#开发者门户#平台工程#2026#DevOps运维