This is a follow-up to:
This post enables the Backstage Kubernetes view without changing any Backstage source code and without installing any extra plugin. We only add Kubernetes-side configuration:
- Add Backstage Kubernetes plugin config (in
app-config.k8s.yaml) - Create a read-only ServiceAccount + RBAC for Backstage to read cluster objects, events, logs, and metrics
- Map the Component to Kubernetes objects using
backstage.io/kubernetes-id - Label the Kubernetes objects with the same
backstage.io/kubernetes-idso they show up under the Component
What you get in the UI
Inside the homelab-backstage Component page → Kubernetes tab:
- Deployments / Pods
- Services / Ingress
- Events
- Pod logs
- Pod + node metrics (if Metrics Server is available)
1. Map the Backstage Component to Kubernetes objects (Catalog annotations)
Update your Component entity so Backstage knows which cluster, which namespace, and which kubernetes-id to query.
catalog-info.yaml
diff --git a/catalog-info.yaml b/catalog-info.yaml
index a8abe33..164d342 100644
--- a/catalog-info.yaml
+++ b/catalog-info.yaml
@@ -8,7 +8,9 @@ metadata:
jenkins.io/job-full-name: backstage/homelab-backstage-main-ci
backstage.io/techdocs-ref: dir:.
harbor.maksonlee.com/repository: backstage/homelab-backstage
-
+ backstage.io/kubernetes-id: homelab-backstage
+ backstage.io/kubernetes-namespace: backstage
+ backstage.io/kubernetes-cluster: in-cluster
spec:
type: website
owner: user:default/maksonlee2. Add Kubernetes plugin config (in-cluster)
Backstage needs to know how to locate clusters and how to authenticate. Here we use:
serviceLocatorMethod: multiTenantclusterLocatorMethods: configwith a single clusterin-clusterauthProvider: serviceAccountso it uses the Pod’s ServiceAccount token automatically
kubernetes/app-config.k8s.yaml
diff --git a/kubernetes/app-config.k8s.yaml b/kubernetes/app-config.k8s.yaml
index 3feca55..04d5aa4 100644
--- a/kubernetes/app-config.k8s.yaml
+++ b/kubernetes/app-config.k8s.yaml
@@ -7,3 +7,16 @@ backend:
cors:
origin: https://backstage-k8s.maksonlee.com
credentials: true
+
+kubernetes:
+ serviceLocatorMethod:
+ type: multiTenant
+
+ clusterLocatorMethods:
+ - type: config
+ clusters:
+ - name: in-cluster
+ url: https://kubernetes.default.svc
+ authProvider: serviceAccount
+ skipTLSVerify: false
+ skipMetricsLookup: false3. Grant Backstage read-only access to Kubernetes (RBAC)
The Kubernetes tab can show more than just Deployments/Pods; common “extra info” includes:
- Events
- Pod logs
- Pod + node metrics (from
metrics.k8s.io)
So we add a read-only ServiceAccount and RBAC.
kubernetes/backstage-k8s-rbac.yaml (new file)
diff --git a/kubernetes/backstage-k8s-rbac.yaml b/kubernetes/backstage-k8s-rbac.yaml
new file mode 100644
index 0000000..05c4034
--- /dev/null
+++ b/kubernetes/backstage-k8s-rbac.yaml
@@ -0,0 +1,47 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: backstage-k8s-reader
+ namespace: backstage
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: backstage-k8s-readonly
+rules:
+ - apiGroups: ['', 'apps', 'batch', 'autoscaling', 'networking.k8s.io']
+ resources:
+ - namespaces
+ - pods
+ - pods/log
+ - services
+ - endpoints
+ - configmaps
+ - limitranges
+ - resourcequotas
+ - deployments
+ - replicasets
+ - daemonsets
+ - statefulsets
+ - jobs
+ - cronjobs
+ - horizontalpodautoscalers
+ - ingresses
+ - events
+ verbs: ['get', 'list', 'watch']
+ - apiGroups: ['metrics.k8s.io']
+ resources: ['pods', 'nodes']
+ verbs: ['get', 'list']
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: backstage-k8s-readonly
+subjects:
+ - kind: ServiceAccount
+ name: backstage-k8s-reader
+ namespace: backstage
+roleRef:
+ kind: ClusterRole
+ name: backstage-k8s-readonly
+ apiGroup: rbac.authorization.k8s.io4. Label Kubernetes resources with backstage.io/kubernetes-id
Backstage links Kubernetes objects to a Component by matching the annotation:
- backstage.io/kubernetes-id: homelab-backstage
…to Kubernetes resources that contain the label:
- backstage.io/kubernetes-id: homelab-backstage
So the fix is: add this label to all workloads/services you want to appear under that Component.
kubernetes/homelab-backstage.yaml
diff --git a/kubernetes/homelab-backstage.yaml b/kubernetes/homelab-backstage.yaml
index 02ab74c..38beb02 100644
--- a/kubernetes/homelab-backstage.yaml
+++ b/kubernetes/homelab-backstage.yaml
@@ -3,6 +3,9 @@ kind: Deployment
metadata:
name: homelab-backstage
namespace: backstage
+ labels:
+ app: homelab-backstage
+ backstage.io/kubernetes-id: homelab-backstage
spec:
replicas: 1
selector:
@@ -12,7 +15,9 @@ spec:
metadata:
labels:
app: homelab-backstage
+ backstage.io/kubernetes-id: homelab-backstage
spec:
+ serviceAccountName: backstage-k8s-reader
initContainers:
- name: wait-for-postgres
image: postgres:16
@@ -30,8 +35,6 @@ spec:
ports:
- name: http
containerPort: 7007
-
- # Load env vars (OIDC client for k8s, plus your existing secrets)
envFrom:
- secretRef:
name: backstage-env
@@ -64,6 +67,9 @@ kind: Service
metadata:
name: homelab-backstage
namespace: backstage
+ labels:
+ app: homelab-backstage
+ backstage.io/kubernetes-id: homelab-backstage
spec:
type: ClusterIP
selector:
@@ -79,6 +85,9 @@ kind: Ingress
metadata:
name: homelab-backstage
namespace: backstage
+ labels:
+ app: homelab-backstage
+ backstage.io/kubernetes-id: homelab-backstage
spec:
ingressClassName: traefik
rules:5. Label Postgres resources so they also appear under the same Component
Same idea: if Postgres is part of this Backstage “system”, label it with the same kubernetes-id.
kubernetes/postgres.yaml
diff --git a/kubernetes/postgres.yaml b/kubernetes/postgres.yaml
index 58300c6..630621a 100644
--- a/kubernetes/postgres.yaml
+++ b/kubernetes/postgres.yaml
@@ -3,6 +3,9 @@ kind: Service
metadata:
name: backstage-postgres
namespace: backstage
+ labels:
+ app: backstage-postgres
+ backstage.io/kubernetes-id: homelab-backstage
spec:
type: ClusterIP
selector:
@@ -18,6 +21,9 @@ kind: StatefulSet
metadata:
name: backstage-postgres
namespace: backstage
+ labels:
+ app: backstage-postgres
+ backstage.io/kubernetes-id: homelab-backstage
spec:
serviceName: backstage-postgres
replicas: 1
@@ -28,6 +34,7 @@ spec:
metadata:
labels:
app: backstage-postgres
+ backstage.io/kubernetes-id: homelab-backstage
spec:
securityContext:
fsGroup: 9996. Include the new RBAC manifest in Kustomize
kubernetes/kustomization.yaml
diff --git a/kubernetes/kustomization.yaml b/kubernetes/kustomization.yaml
index 1057fcd..28ba11a 100644
--- a/kubernetes/kustomization.yaml
+++ b/kubernetes/kustomization.yaml
@@ -6,6 +6,7 @@ namespace: backstage
resources:
- postgres.yaml
- homelab-backstage.yaml
+ - backstage-k8s-rbac.yaml
generatorOptions:
disableNameSuffixHash: false7. Apply and verify
Apply:
kubectl apply -k kubernetes/
kubectl -n backstage rollout status deploy/homelab-backstageVerify Backstage can read objects (quick sanity checks):
kubectl -n backstage get sa backstage-k8s-reader
kubectl get clusterrole backstage-k8s-readonly
kubectl get clusterrolebinding backstage-k8s-readonlyThen open Backstage:
- Catalog → homelab-backstage → Kubernetes tab

