Kubernetes Ingestor is a Backstage backend plugin that turns live Kubernetes resources into Backstage catalog entities. It discovers standard workloads and custom resources, then keeps the catalog in sync as things change in your clusters. It handles Crossplane claims and XRDs out of the box, so platform and app teams see the same source of truth inside Backstage.
Beyond discovery, the plugin can generate software templates from XRDs. You can publish those to GitHub, GitLab, or Bitbucket, or download the YAML when needed. It also creates API entities for XRDs, maps relationships between components and the APIs they provide or consume, and supports multi cluster setups and namespace filters. If you rely on custom GVKs, you can add them so they become first class catalog entries as well.
Typical use cases include pulling existing services into Backstage without hand writing catalog files, keeping metadata fresh during frequent deploys, and offering Crossplane driven templates that fit a GitOps flow with pull requests. Teams can control scope with annotations and choose opt in or opt out ingestion patterns. You get a simple way to organize systems and relationships while the plugin does the heavy lifting behind the scenes. Setup expects cluster access and the right permissions.
Installation Instructions
These instructions apply to self-hosted Backstage only.
Install with the new backend system
Add dependencies
yarn --cwd packages/backend add @terasky/backstage-plugin-kubernetes-ingestor
yarn --cwd packages/backend add @terasky/backstage-plugin-scaffolder-backend-module-terasky-utils
yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-github @backstage/plugin-scaffolder-backend-module-gitlab @backstage/plugin-scaffolder-backend-module-bitbucket
Register the backend modules
Edit packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
// Kubernetes Ingestor backend module
backend.add(import('@terasky/backstage-plugin-kubernetes-ingestor'));
// Scaffolder modules for template publishing
backend.add(import('@backstage/plugin-scaffolder-backend-module-github'));
backend.add(import('@backstage/plugin-scaffolder-backend-module-gitlab'));
backend.add(import('@backstage/plugin-scaffolder-backend-module-bitbucket'));
backend.add(import('@terasky/backstage-plugin-scaffolder-backend-module-terasky-utils'));
backend.start();
Configure the plugin
Add to app-config.yaml
kubernetesIngestor:
mappings:
namespaceModel: 'cluster'
nameModel: 'name-cluster'
titleModel: 'name'
systemModel: 'namespace'
referencesNamespaceModel: 'default'
components:
enabled: true
taskRunner:
frequency: 10
timeout: 600
excludedNamespaces:
- kube-public
- kube-system
customWorkloadTypes:
- group: pkg.crossplane.io
apiVersion: v1
plural: providers
crossplane:
enabled: true
claims:
ingestAllClaims: true
xrds:
enabled: true
publishPhase:
allowedTargets: ['github.com', 'gitlab.com']
target: github
git:
repoUrl: github.com?owner=org&repo=templates
targetBranch: main
allowRepoSelection: true
taskRunner:
frequency: 10
timeout: 600
Configure your clusters
Point Backstage to your Kubernetes clusters and use the service account token
kubernetes:
clusterLocatorMethods:
- type: config
clusters:
- name: prod
url: https://your-api-server
authProvider: serviceAccount
serviceAccountToken: ${K8S_SA_TOKEN}
skipTLSVerify: true
You can set the token through an env var
export K8S_SA_TOKEN='paste-your-token'
Set Kubernetes RBAC and service account
You can apply a full setup in one go
kubectl apply --filename - <<'EOF'
apiVersion: v1
kind: Namespace
metadata:
name: backstage-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: backstage-user
namespace: backstage-system
---
apiVersion: v1
kind: Secret
metadata:
name: backstage-token
namespace: backstage-system
annotations:
kubernetes.io/service-account.name: backstage-user
type: kubernetes.io/service-account-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: backstage-kubernetes-ingestor-rbac
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: view
subjects:
- kind: ServiceAccount
name: backstage-user
namespace: backstage-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: backstage-crd-viewer
rules:
- apiGroups:
- apiextensions.k8s.io
resources:
- customresourcedefinitions
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: backstage-crossplane-ingestion-crd-rbac
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: backstage-crd-viewer
subjects:
- kind: ServiceAccount
name: backstage-user
namespace: backstage-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: backstage-crossplane-ingestion-rbac
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: crossplane-view
subjects:
- kind: ServiceAccount
name: backstage-user
namespace: backstage-system
EOF
Fetch the token
kubectl get secret -n backstage-system backstage-token -o jsonpath='{.data.token}' | base64 --decode
Paste the token into the serviceAccountToken field in app-config.yaml or export it as K8S_SA_TOKEN
Set Git access for template publishing
export GITHUB_TOKEN=your-token
export GITLAB_TOKEN=your-token
export BITBUCKET_TOKEN=your-token
Install with the old backend system
Support status
This plugin ships as a backend module for the new backend system. It does not include a legacy router. The simplest path in a legacy repo is to adopt the new backend entry point inside packages backend. You can keep your existing code in git and move over in one change.
Quick path in a legacy repo
Replace your backend bootstrap with the new backend entry point. Then add the module as shown above
Edit packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
backend.add(import('@backstage/plugin-app-backend'));
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-scaffolder-backend'));
// Add the Kubernetes Ingestor module
backend.add(import('@terasky/backstage-plugin-kubernetes-ingestor'));
// Add publishing modules
backend.add(import('@backstage/plugin-scaffolder-backend-module-github'));
backend.add(import('@backstage/plugin-scaffolder-backend-module-gitlab'));
backend.add(import('@backstage/plugin-scaffolder-backend-module-bitbucket'));
backend.add(import('@terasky/backstage-plugin-scaffolder-backend-module-terasky-utils'));
backend.start();
Keep the same app-config.yaml and RBAC from the new backend steps
Use it in the frontend
Make sure the Catalog and Scaffolder pages are visible
The plugin creates entities in the catalog. It also registers software templates. You view them on the Catalog and Create pages. Add these routes if your app does not have them
Edit packages/app/src/App.tsx
import React from 'react';
import { FlatRoutes } from '@backstage/core-components';
import { CatalogIndexPage, CatalogEntityPage } from '@backstage/plugin-catalog';
import { ScaffolderPage } from '@backstage/plugin-scaffolder';
import { Route } from 'react-router';
export const AppRoutes = () => (
<FlatRoutes>
<Route path="/catalog" element={<CatalogIndexPage />} />
<Route path="/catalog/:namespace/:kind/:name" element={<CatalogEntityPage />} />
<Route path="/create" element={<ScaffolderPage />} />
</FlatRoutes>
);
If you do not have the frontend packages installed, add them
yarn --cwd packages/app add @backstage/plugin-catalog @backstage/plugin-scaffolder
No extra frontend import is needed for this plugin. The backend will populate the catalog and templates. You will see components appear in Catalog. You will see generated templates on the Create page
Optional Crossplane setup notes
If you run Crossplane and want claim and XR ingestion with template generation, keep crossplane enabled in the config shown above. The RBAC section already grants the needed access for CRDs and Crossplane resources
Optional configuration tweaks
You can narrow ingestion to specific clusters
kubernetesIngestor:
allowedClusterNames:
- prod
You can add more custom workload types
kubernetesIngestor:
components:
customWorkloadTypes:
- group: apps.example.com
apiVersion: v1
plural: applications
singular: application
You can switch to opt in ingestion
kubernetesIngestor:
components:
onlyIngestAnnotatedResources: true
Changelog
This changelog is produced from commits made to the Kubernetes Ingestor plugin since 4 months ago, and based on the code located here. It may not contain information about all commits. Releases and version bumps are intentionally omitted. This changelog is generated by AI.
Features
- Add annotations terasky.backstage.io/name and terasky.backstage.io/title to override the generated component name and title for claims XRs and Kubernetes resources #82 merged 27 days ago
- Add cluster and kind tags for XR and claim based components #82 merged 27 days ago
Improvements
- Change architecture to use the Kubernetes backend plugin proxy endpoint The plugin now relies on the backend for auth and cluster discovery This supports custom cluster locators and custom auth that work with the backend proxy #82 merged 27 days ago
Bug fixes
- Fix an issue where kind and apiVersion are not always returned for some Kubernetes workload types #82 merged 27 days ago
Compatibility
- Support Backstage 1.42.5 #82 merged 27 days ago
Set up Backstage in minutes with Roadie
Focus on using Backstage, rather than building and maintaining it.