Flux logo

Backstage Flux Plugin

Created by Weaveworks

Flux Plugins brings Flux into Backstage. It is a set of UI pieces you add to your portal. They read Flux resources from your Kubernetes clusters and show them next to each service. You get clear views of Kustomizations, HelmReleases, and the sources that feed them such as GitRepositories, OCIRepositories, and HelmRepositories. The plugin uses the common Kubernetes label in Backstage to map resources to the current entity. That avoids custom wiring.

The plugin offers cards you can place on service pages, plus a Flux runtime page for a cluster wide snapshot. From one screen you can check status, revision, last reconcile, and errors. With the right permissions you can trigger a sync or suspend and resume a resource during incident work. If you use Weave GitOps you can jump from the card to deeper details. Some resources show a verification status when signature checks are set up.

Typical use cases include giving service owners a trusted picture of what is running, helping platform teams monitor many clusters from Backstage, and cutting context switching during triage. It fits teams that already use FluxCD for GitOps and want that data in their developer portal. The result is faster checks and fewer tabs.

Installation Instructions

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

Step 1 Install frontend packages

Copy
yarn add --cwd packages/app @weaveworksoss/backstage-plugin-flux @backstage/plugin-kubernetes

Step 2 Install the Kubernetes backend

New backend system

Copy
yarn add --cwd packages/backend @backstage/plugin-kubernetes-backend @backstage/backend-defaults

Edit packages/backend/src/index.ts

Copy
// New backend system
import { createBackend } from '@backstage/backend-defaults';
import { kubernetesPlugin } from '@backstage/plugin-kubernetes-backend';

const backend = createBackend();

backend.add(kubernetesPlugin());

backend.start();

Old backend system

Copy
yarn add --cwd packages/backend @backstage/plugin-kubernetes-backend

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,
    discovery: env.discovery,
    tokenManager: env.tokenManager,
    catalogApi: env.catalogClient,
  });
}

Wire it in packages/backend/src/index.ts

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

// inside main bootstrap
const kubernetesEnv = useHotMemoize(module, () => createEnv('kubernetes'));
apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv));

Step 3 Configure clusters for the Kubernetes plugin

Edit app-config.yaml

Copy
kubernetes:
  serviceLocatorMethod:
    type: 'multiTenant'
  clusterLocatorMethods:
    - type: 'config'
      clusters:
        - url: https://192.168.0.1:8000
          name: Default
          authProvider: 'serviceAccount'
          skipTLSVerify: true
          skipMetricsLookup: true
          serviceAccountToken: ABC123
          caData: LS0tLS1CRUdJTiBDRVJUSUZJQ0...

If you use a ServiceAccount in config you must bind it to the ClusterRole created by Flux

Copy
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: backstage-cluster-view-rolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: flux-view-flux-system
subjects:
  - kind: ServiceAccount
    name: backstage
    namespace: flux-system

To enable sync suspend resume buttons grant patch to Flux resources

Copy
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: patch-flux-resources
rules:
  - apiGroups:
      - source.toolkit.fluxcd.io
    resources:
      - buckets
      - helmcharts
      - gitrepositories
      - helmrepositories
      - ocirepositories
    verbs:
      - patch
  - apiGroups:
      - kustomize.toolkit.fluxcd.io
    resources:
      - kustomizations
    verbs:
      - patch
  - apiGroups:
      - helm.toolkit.fluxcd.io
    resources:
      - helmreleases
    verbs:
      - patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: backstage-patch-flux-resources-rolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: patch-flux-resources
subjects:
  - kind: ServiceAccount
    name: backstage
    namespace: flux-system

Step 4 Add the Kubernetes tab to your entity pages

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

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

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

export default serviceEntityPage;

Step 5 Add Flux cards to your entity pages

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

Minimal cards that show deployments and sources

Copy
import React from 'react';
import Grid from '@material-ui/core/Grid';
import { EntityLayout } from '@backstage/plugin-catalog';
import {
  EntityFluxDeploymentsCard,
  EntityFluxSourcesCard,
} from '@weaveworksoss/backstage-plugin-flux';

const overviewContent = (
  <Grid container spacing={3} alignItems="stretch">
    <Grid item md={6} xs={12}>
      <EntityFluxDeploymentsCard />
    </Grid>
    <Grid item md={6} xs={12}>
      <EntityFluxSourcesCard />
    </Grid>
  </Grid>
);

const serviceEntityPage = (
  <EntityLayout>
    <EntityLayout.Route path="/" title="Overview">
      {overviewContent}
    </EntityLayout.Route>

    <EntityLayout.Route path="/kubernetes" title="Kubernetes">
      {/* keep the Kubernetes tab from the previous step */}
    </EntityLayout.Route>
  </EntityLayout>
);

export default serviceEntityPage;

Optional per resource cards

Copy
import {
  EntityFluxHelmReleasesCard,
  EntityFluxKustomizationsCard,
  EntityFluxGitRepositoriesCard,
  EntityFluxOCIRepositoriesCard,
  EntityFluxHelmRepositoriesCard,
  EntityFluxImagePoliciesCard,
} from '@weaveworksoss/backstage-plugin-flux';

const fluxDetailsContent = (
  <Grid container spacing={3} alignItems="stretch">
    <Grid item md={12}>
      <EntityFluxHelmReleasesCard />
    </Grid>
    <Grid item md={12}>
      <EntityFluxKustomizationsCard />
    </Grid>
    <Grid item md={12}>
      <EntityFluxGitRepositoriesCard />
    </Grid>
    <Grid item md={12}>
      <EntityFluxOCIRepositoriesCard />
    </Grid>
    <Grid item md={12}>
      <EntityFluxHelmRepositoriesCard />
    </Grid>
    <Grid item md={12}>
      <EntityFluxImagePoliciesCard />
    </Grid>
  </Grid>
);

To simplify a table for small lists set many to false

Copy
<Grid item md={4} xs={12}>
  <EntityFluxHelmReleasesCard many={false} />
</Grid>

Step 6 Label your catalog entities and Flux resources

Add the Kubernetes id annotation to your Backstage entity

Copy
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: carts-service
  annotations:
    backstage.io/kubernetes-id: carts-service
spec:
  type: service
  owner: sockshop-team

Label your Flux objects with the same id example for a HelmRelease

Copy
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: carts-nginx
  namespace: carts
  labels:
    backstage.io/kubernetes-id: carts-service
spec:
  chart:
    spec:
      chart: nginx
      reconcileStrategy: ChartVersion
      sourceRef:
        kind: HelmRepository
        name: podinfo
  interval: 1m0s

Add linking and action control in app-config.yaml

Copy
gitops:
  baseUrl: https://wego.example.com

Set read only mode to disable sync suspend resume buttons in the UI

Copy
gitops:
  readOnly: true

Keep cluster names in your Kubernetes config accurate because generated links include the cluster name from the Backstage cluster configuration

Step 8 Add the Flux runtime page and nav item optional

Route in packages/app/src/App.tsx

Copy
import React from 'react';
import { FlatRoutes } from '@backstage/core-app-api';
import { Route } from 'react-router';
import { FluxRuntimePage } from '@weaveworksoss/backstage-plugin-flux';

export const routes = (
  <FlatRoutes>
    {/* other routes */}
    <Route path="/flux-runtime" element={<FluxRuntimePage />} />
  </FlatRoutes>
);

Nav item in packages/app/src/components/Root/Root.tsx

Copy
import React, { PropsWithChildren } from 'react';
import { SidebarPage, Sidebar, SidebarGroup, SidebarItem, SidebarScrollWrapper } from '@backstage/core-components';
import MenuIcon from '@material-ui/icons/Menu';
import { FluxIcon } from '@weaveworksoss/backstage-plugin-flux';

export const Root = ({ children }: PropsWithChildren<{}>) => (
  <SidebarPage>
    <Sidebar>
      <SidebarGroup label="Menu" icon={<MenuIcon />}>
        <SidebarScrollWrapper>
          <SidebarItem icon={FluxIcon} to="flux-runtime" text="Flux Runtime" />
        </SidebarScrollWrapper>
      </SidebarGroup>
    </Sidebar>
    {children}
  </SidebarPage>
);

Notes on auth

If you see unauthorized calls to the Kubernetes proxy you likely need user authentication in front of Backstage so requests include a Backstage token. Configure an auth provider in your app to provide tokens for the proxy calls.

Changelog

The Flux plugin has not seen any significant changes since a year ago.

Set up Backstage in minutes with Roadie