Backstage Kubernetes logo

Backstage Backstage Kubernetes Plugin

Created by Spotify

Backstage Kubernetes brings your cluster view into the Backstage catalog. It is built for service owners. You can check the health of a service in one place across many clusters. The plugin highlights problems and lets you drill into deployments and pods for that service.

It connects to Kubernetes through the API and works with any cloud provider or managed platform.Use it to watch rollouts during a release. See errors sooner and reduce context switching. Bring runtime details next to docs, APIs, and ownership so teams can act faster.

For most teams the value is a clear picture of what is running, where it runs, and whether it is healthy.

A screenshot of the Kubernetes plugin.

Installation Instructions

These instructions apply to self-hosted Backstage only. To use this plugin on Roadie, visit the docs.

Install the frontend package

  1. Add the plugin to the app package
Copy
yarn add --cwd packages/app @backstage/plugin-kubernetes
  1. If you use the new frontend system then add the plugin to your app features

Edit packages/app/src/App.tsx

Copy
import { createApp } from '@backstage/app-defaults';
import kubernetesPlugin from '@backstage/plugin-kubernetes/alpha';

export const app = createApp({
  features: [
    kubernetesPlugin,
  ],
});
  1. If you use the classic frontend system then add the Kubernetes tab to the entity page

Edit packages/app/src/components/catalog/EntityPage.tsx

Copy
import React from 'react';
import { EntityLayout } from '@backstage/plugin-catalog';
import {
  EntityKubernetesContent,
  isKubernetesAvailable,
} from '@backstage/plugin-kubernetes';

const serviceEntityPage = (
  <EntityLayout>
    {/* other tabs */}
    <EntityLayout.Route
      path="/kubernetes"
      title="Kubernetes"
      if={isKubernetesAvailable}
    >
      <EntityKubernetesContent
        // optional
        refreshIntervalMs={30000}
      />
    </EntityLayout.Route>
  </EntityLayout>
);

export default serviceEntityPage;
  1. If you use the classic frontend system and your app defines custom APIs then register the Kubernetes APIs

Edit packages/app/src/apis.ts

Copy
import {
  createApiFactory,
  discoveryApiRef,
  fetchApiRef,
} from '@backstage/core-plugin-api';

import {
  kubernetesApiRef,
  KubernetesBackendClient,
  kubernetesAuthProvidersApiRef,
  KubernetesAuthProviders,
  kubernetesProxyApiRef,
  KubernetesProxyClient,
} from '@backstage/plugin-kubernetes';

export const apis = [
  // other apis

  createApiFactory({
    api: kubernetesAuthProvidersApiRef,
    deps: {
      // Provide the auth providers your app supports
      // You can omit any providers you do not use
      // For example googleAuthApi microsoftAuthApi githubAuthApi gitlabAuthApi oktaAuthApi
    } as any,
    factory: deps => new KubernetesAuthProviders(deps as any),
  }),

  createApiFactory({
    api: kubernetesApiRef,
    deps: {
      discoveryApi: discoveryApiRef,
      fetchApi: fetchApiRef,
      kubernetesAuthProvidersApi: kubernetesAuthProvidersApiRef,
    },
    factory: ({ discoveryApi, fetchApi, kubernetesAuthProvidersApi }) =>
      new KubernetesBackendClient({
        discoveryApi,
        fetchApi,
        kubernetesAuthProvidersApi,
      }),
  }),

  createApiFactory({
    api: kubernetesProxyApiRef,
    deps: { kubernetesApi: kubernetesApiRef },
    factory: ({ kubernetesApi }) =>
      new KubernetesProxyClient({ kubernetesApi }),
  }),
];
  1. If you use the new frontend system then enable the entity content extension

Edit app-config.yaml

Copy
app:
  extensions:
    - entity-content:kubernetes/kubernetes

You can change where the tab shows with a filter

Copy
app:
  extensions:
    - entity-content:kubernetes/kubernetes:
        config:
          filter: kind:component,resource

Install the backend package

New backend system

  1. Add the backend package
Copy
yarn add --cwd packages/backend @backstage/plugin-kubernetes-backend
  1. Register the backend plugin in your backend entry

Edit packages/backend/src/index.ts

Copy
import { createBackend } from '@backstage/backend-defaults';

const backend = createBackend();

// Kubernetes backend
backend.add(
  import('@backstage/plugin-kubernetes-backend').then(m => m.kubernetesPlugin()),
);

backend.start();

Classic backend system

  1. Add the backend package
Copy
yarn add --cwd packages/backend @backstage/plugin-kubernetes-backend
  1. Create the plugin router

Create packages/backend/src/plugins/kubernetes.ts

Copy
import { createRouter } from '@backstage/plugin-kubernetes-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';

export default async function createPlugin(
  env: PluginEnvironment,
): Promise<Router> {
  return await createRouter({
    logger: env.logger,
    config: env.config,
    catalogApi: env.catalogClient,
    discovery: env.discovery,
    permissions: env.permissions,
    httpAuth: env.httpAuth,
    identity: env.identity,
  });
}
  1. Mount the router

Edit packages/backend/src/index.ts

Copy
import kubernetes from './plugins/kubernetes';

// inside main bootstrap
const apiRouter = Router();
// other routers
apiRouter.use('/kubernetes', await kubernetes(env));

Configure clusters in app config

Add your clusters and service locator

Edit app-config.yaml

Copy
kubernetes:
  serviceLocatorMethod:
    type: multiTenant
  clusterLocatorMethods:
    - type: config
      clusters:
        - url: https://your-cluster.example.com
          name: my-cluster
          authProvider: serviceAccount
          skipTLSVerify: false
          serviceAccountToken:
            $env: K8S_SERVICE_ACCOUNT_TOKEN
          dashboardUrl: https://k8s-ui.example.com
          # optional if your cluster does not have metrics server
          skipMetricsLookup: false

Notes

  • authProvider can be serviceAccount aws aks oidc azure google among others depending on your environment
  • For local dev you can use localKubectlProxy in place of url

Annotate catalog entities

Add Kubernetes annotations to the catalog entity so the tab knows what to load

Use id match

Copy
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: my-service
  annotations:
    backstage.io/kubernetes-id: my-service
spec:
  type: service
  owner: team-a
  lifecycle: production

Or use a label selector

Copy
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: my-service
  annotations:
    backstage.io/kubernetes-label-selector: 'app=my-service'
spec:
  type: service
  owner: team-a
  lifecycle: production

Optional tweaks

Change the tab refresh interval in the classic frontend

Copy
<EntityKubernetesContent refreshIntervalMs={15000} />

Show the tab on more kinds in the new frontend system

Copy
app:
  extensions:
    - entity-content:kubernetes/kubernetes:
        config:
          filter: kind:component,api,resource,system

Where you should see the plugin

  • In the classic frontend you will get a Kubernetes tab on the entity page at path catalog default component yourcomponent kubernetes when the entity passes the isKubernetesAvailable check
  • In the new frontend system you will get a Kubernetes tab once the extension is enabled and the entity filter matches

Things to Know

The Backstage Kubernetes plugin has two separate components:

  • frontend: it will take care of displaying the information to the user.
  • backend: it will take care of connecting to the Kubernetes clusters and sending the information to the frontend.

After installing the plugins, you have to configure them in two steps:

  1. Allow the backend to collect objects from your Kubernetes cluster(s).
  2. Surfacing your Kubernetes objects in catalog entities

Configuring Kubernetes Clusters

Here is a complete example of a configuration entry:

Copy
### app-config.yaml
kubernetes:
  serviceLocatorMethod: 'multiTenant'
  clusterLocatorMethods:
    - 'config'
  clusters:
    - url: http://127.0.0.1:9999
      name: minikube
      authProvider: 'serviceAccount'
      serviceAccountToken:
        $env: K8S_MINIKUBE_TOKEN
    - url: http://127.0.0.2:9999
      name: gke-cluster-1
      authProvider: 'google'

You can find the complete list of fields in the the official Backstage documentation.

Using RBAC Authorization

The current RBAC permissions required are read-only cluster wide, for the following objects:

  • pods
  • services
  • configmaps
  • deployments
  • replicasets
  • horizontalpodautoscalers
  • ingresses

An example of a Role to grant read access to pods:

Copy
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
  - apiGroups: ['']
    resources: ['pods']
    verbs: ['get', 'watch', 'list']

Surfacing your Kubernetes components as part of an entity

There are two ways to surface your Kubernetes components as part of an entity.
NOTE: The label selector takes precedence over the annotation/service id.

Common backstage.io/kubernetes-id label

  1. Adding the metadata annotation

The following annotation must be added so that Backstage can detect that an entity has Kubernetes components.

Copy
### catalog-info.yaml
metadata:
    annotations:
        'backstage.io/kubernetes-id': <ENTITY_NAME>
    ...
  1. Labeling your Kubernetes components

In order for your Kubernetes components to show up in the service catalog as a part of an entity, your Kubernetes components themselves can have the following label:

Copy
"metadata": {
  "labels": {
    "backstage.io/kubernetes-id": <ENTITY_NAME>
    ...
  }
}

Label selector query annotation

Via a label selector, the user can identify a set of objects.
You can write your own custom label selector query that Backstage will use to lookup the objects. You can read Labels and Selectors documentation for more info.

Copy
### catalog-info.yaml
annotations:
  'backstage.io/kubernetes-label-selector': 'app=my-app,component=front-end'

Example of steps to follow

  1. Get the Kubernetes master base url kubectl cluster-info

  2. Get the service account token

    Copy
    kubectl get secret $(kubectl get sa <SERVICE_ACCOUNT_NAME> -o=json \
    | jq -r '.secrets[0].name') -o=json \
    | jq -r '.data["token"]' \
    | base64 --decode \
    | pbcopy
  3. Register existing component in Backstage

    Copy
    # catalog-info.yaml
    apiVersion: backstage.io/v1alpha1
    kind: Component
    metadata:
      name: <ENTITY_NAME>
      annotations:
        'backstage.io/kubernetes-id': <ENTITY_NAME>
    spec:
      type: service
      lifecycle: production
      owner: guest
  4. Add or update app-config.local.yaml with the following:

    Copy
    # app-config.local.yaml
    kubernetes:
      serviceLocatorMethod: 'multiTenant'
      clusterLocatorMethods:
        - 'config'
      clusters:
        - url: <KUBERNETES_MASTER_BASE_URL_FROM_STEP_1>
          name: minikube
          serviceAccountToken: <TOKEN_FROM_STEP_2>
          authProvider: 'serviceAccount'

Changelog

This changelog is produced from commits made to the Backstage Kubernetes plugin since 8 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.

Breaking changes

  • Update Kubernetes client library to 1.1.2 in the plugin dependency tree. This library is now ESM only. If you import it in custom code you may need to adjust your imports or build setup. See #28198 merged 5 months ago

Bug fixes

  • Remove double padding on the Kubernetes entity page. Content now aligns with the entity page layout. See #30490 merged 2 months ago

Dependencies

  • Switch to the scoped @xterm packages in the Kubernetes plugin. Replaces deprecated xterm packages. See #29623 merged 4 months ago
  • Bump @kubernetes client node to 1.1.2. See #28198 merged 5 months ago

Set up Backstage in minutes with Roadie