Skip to main content

Backstage β€” How It Works With Helm and Kustomize Apps

Backstage is the developer portal that aggregates visibility across the platform. It connects to the Kubernetes cluster, ArgoCD, and GitHub to surface a unified view of every service. The integration works differently depending on whether a component is deployed via Helm (third-party tool) or Kustomize (own service).


The three Backstage integration points​

Regardless of deployment method, Backstage uses the same three mechanisms:

MechanismWhat it doesConfigured via
CatalogRegisters the component (name, description, links, owner)catalog-info.yaml in a repo
Kubernetes pluginShows live pod status, restarts, image tagbackstage.io/kubernetes-id label on pods
ArgoCD pluginShows sync status, health, last deployed commitargocd/app-name annotation in catalog entry

Custom apps deployed via Kustomize​

platform-demo is the reference example. Backstage has full end-to-end visibility because the team owns every layer.

How it is wired​

1. Catalog entry lives in the service repo.

The platform-demo GitHub repo has a catalog-info.yaml at its root. Backstage reads it directly from GitHub:

# platform-demo/catalog-info.yaml
metadata:
name: platform-demo
annotations:
backstage.io/kubernetes-id: platform-demo # matches pod label
argocd/app-name: platform-demo # ArgoCD app name
github.com/project-slug: andrelair-platform/platform-demo

2. The pod label is set in the Kustomize base.

The base/deployment.yaml pod template carries the matching label:

# services/platform-demo/base/deployment.yaml
spec:
template:
metadata:
labels:
backstage.io/kubernetes-id: platform-demo

Because the label is in the base, it is inherited by all three overlays automatically β€” dev, staging, and prod pods all carry it. Backstage shows pods from all three namespaces on the same component page.

3. CI/CD tab is populated automatically.

The github.com/project-slug annotation causes Backstage to query the GitHub API for recent workflow runs. No extra configuration needed.

What you see in Backstage​

Component: platform-demo
β”œβ”€β”€ Overview ← description, tags, live URL, docs link, ArgoCD link
β”œβ”€β”€ CI/CD ← recent GitHub Actions runs (build, test, push, bump)
β”œβ”€β”€ Kubernetes ← pods in platform-demo-dev / platform-demo-staging / gitops-demo
β”‚ each pod: status, restarts, image SHA, CPU/memory
└── ArgoCD ← sync status, health, last commit SHA, diff link

Helm-deployed tools (open source)​

For third-party tools deployed via Helm β€” Vault, Grafana, ArgoCD, Open WebUI, Ollama, Langfuse β€” the catalog entry is written in minicloud-gitops/catalog-info.yaml rather than in the upstream chart repository (which you don't control).

The pod label must be injected via the Helm values file, using whatever key the chart exposes for pod-level labels.

Label key by chart​

Different charts use different keys:

ToolChartValues key
Open WebUIopen-webui/open-webuipodLabels:
Ollamaotwld/ollamapodLabels:
Vaulthashicorp/vaultserver.extraLabels:
Grafana (via kps)prometheus-community/kube-prometheus-stackgrafana.podLabels:
ArgoCD serverargoproj/argo-cdserver.podLabels:
Langfuselangfuse/langfuselangfuse.labels:

Example for Vault (helm-values/vault-values.yaml):

server:
extraLabels:
backstage.io/kubernetes-id: vault

Example for Open WebUI (helm-values/open-webui-values.yaml):

podLabels:
backstage.io/kubernetes-id: open-webui

Catalog entry lives in minicloud-gitops​

Because there is no source repo to put a catalog-info.yaml in, the entry goes in minicloud-gitops/catalog-info.yaml:

# minicloud-gitops/catalog-info.yaml
---
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: vault
annotations:
backstage.io/kubernetes-id: vault # matches server.extraLabels
argocd/app-name: vault # ArgoCD app name
github.com/project-slug: andrelair-platform/minicloud-gitops
spec:
type: service
lifecycle: production
owner: andrelair-platform
system: minicloud-platform

What you see in Backstage​

Component: vault
β”œβ”€β”€ Overview ← description, tags, Vault UI link, ArgoCD link
β”œβ”€β”€ Kubernetes ← vault-0 pod: status, restarts, image tag
└── ArgoCD ← sync status, health, last commit

Component: grafana
β”œβ”€β”€ Overview ← description, tags, Grafana UI link
β”œβ”€β”€ Kubernetes ← grafana pod: status, restarts
└── ArgoCD ← kube-prometheus-stack sync status

Component: open-webui
β”œβ”€β”€ Overview ← description, links (internal + public URL)
β”œβ”€β”€ Kubernetes ← open-webui pod across the ai namespace
└── ArgoCD ← sync status

No CI/CD tab β€” there are no GitHub Actions workflows in minicloud-gitops for these tools. The tab only appears when github.com/project-slug points to a repo that has workflow runs.


Side-by-side comparison​

Custom app (Kustomize)Helm tool
catalog-info.yamlin the service's own repoin minicloud-gitops
Pod label added inbase deployment.yamlHelm values file (podLabels: / extraLabels: etc.)
Label key varies by chartno β€” you set it directlyyes β€” check the chart's values schema
CI/CD tabyes β€” GitHub Actions in the service repono
ArgoCD tabyesyes
Kubernetes tabyes β€” all envs (dev + staging + prod)yes β€” single namespace
Who writes the catalog entryservice teamplatform team

Registered components (current state)​

Custom apps β€” full integration​

ComponentKubernetes labelArgoCD app
platform-demobackstage.io/kubernetes-id: platform-demoplatform-demo
event-demobackstage.io/kubernetes-id: echo-workerevent-demo
homerbackstage.io/kubernetes-id: homerhomer
whoamibackstage.io/kubernetes-id: whoamiwhoami

Helm tools β€” Kubernetes + ArgoCD integration​

ComponentValues file keyArgoCD app
vaultserver.extraLabelsvault
grafanagrafana.podLabels (in kps values)kube-prometheus-stack
argocdserver.podLabelsargo-cd
open-webuipodLabelsopen-webui
ollamapodLabelsollama
langfuselangfuse.labelslangfuse

Adding a new component to Backstage​

New custom service (Kustomize)​

  1. Add catalog-info.yaml to the service repo root with backstage.io/kubernetes-id: <name> annotation.
  2. Add backstage.io/kubernetes-id: <name> label to the pod template in services/<name>/base/deployment.yaml.
  3. Add the catalog URL to backstage.appConfig.catalog.locations in helm-values/backstage-values.yaml.

New Helm tool​

  1. Check the chart's default values for the pod label key: helm show values <repo>/<chart> | grep podLabels
  2. Add the label to helm-values/<tool>-values.yaml.
  3. Add a catalog entry to minicloud-gitops/catalog-info.yaml.
  4. Commit and push β€” ArgoCD syncs the label into the pods; Backstage picks up the new catalog entry on its next refresh cycle (~1 min).

Troubleshooting​

Kubernetes tab is empty:

  • Check the pod carries the label: kubectl get pod <pod> -o jsonpath='{.metadata.labels}'
  • Check the value in catalog-info.yaml matches exactly (case-sensitive)
  • Check the Backstage service account has get/list/watch on pods in that namespace

ArgoCD tab shows "App not found":

  • Verify argocd/app-name annotation matches exactly the ArgoCD Application name
  • The ArgoCD plugin uses the credentials in backstage-phase24-secret β€” verify they are still valid

Catalog entry not appearing:

  • Check the URL in backstage.appConfig.catalog.locations returns HTTP 200
  • Check Backstage logs: kubectl logs -n backstage -l app.kubernetes.io/name=backstage