ScaleOps logo

Backstage ScaleOps Plugin

Created by terasky.com

ScaleOps is a platform for Kubernetes resource optimization. It tunes pod requests in real time, adjusts replicas, improves placement, and tracks costs. The goal is simple. Keep performance steady while cutting waste. It works across many clusters and can run in your own environment.

The ScaleOps Backstage plugin brings that context into your service pages. It shows potential savings and realized savings for the Kubernetes pieces tied to a component. You can jump to the ScaleOps dashboard for deeper views when you need them. The plugin works with a single ScaleOps endpoint and supports multi cluster setups, so teams get a clear picture across environments inside Backstage.

Typical use cases start with day to day ownership. A service owner can check where a workload is over requested and see the expected impact of rightsizing before making changes. A FinOps or platform lead can review savings and trends during weekly checks without leaving the portal. During incidents, engineers can confirm whether resources match current demand, then drill into the ScaleOps app for details when required. This keeps cost and capacity data next to docs, builds, and runbooks, which reduces context switching and helps teams act faster.

Installation Instructions

These instructions apply to self-hosted Backstage only.

Install the frontend package

  1. Add the plugin to the app package
Copy
yarn add --cwd packages/app @terasky/backstage-plugin-scaleops-frontend

Add the ScaleOps card to entity pages

  1. Open packages/app/src/components/catalog/EntityPage.tsx

  2. Import the card

Copy
// packages/app/src/components/catalog/EntityPage.tsx
import React from 'react';
import Grid from '@material-ui/core/Grid';
import { EntityLayout } from '@backstage/plugin-catalog';
import { EntitySwitch, isKind } from '@backstage/plugin-catalog';
import { EntityAboutCard } from '@backstage/plugin-catalog';
import { EntityScaleOpsCard } from '@terasky/backstage-plugin-scaleops-frontend';
  1. Render the card on the overview tab for services
Copy
// inside your ServiceEntityPage component in EntityPage.tsx

const serviceOverviewContent = (
  <Grid container spacing={3} alignItems="stretch">
    <Grid item xs={12} md={6}>
      <EntityAboutCard variant="gridItem" />
    </Grid>

    <Grid item xs={12} md={6}>
      <EntityScaleOpsCard />
    </Grid>
  </Grid>
);

export const ServiceEntityPage = () => (
  <EntityLayout>
    <EntityLayout.Route path="/" title="Overview">
      {serviceOverviewContent}
    </EntityLayout.Route>
  </EntityLayout>
);

// ensure you route to ServiceEntityPage for kind Service
export const EntityPage = () => (
  <EntitySwitch>
    <EntitySwitch.Case if={isKind('service')}>
      <ServiceEntityPage />
    </EntitySwitch.Case>
    {/* keep your other cases here */}
  </EntitySwitch>
);

You can also add the card to other kinds if it makes sense in your portal layout

Configure the ScaleOps endpoint through the Backstage proxy

The plugin reads data from a single ScaleOps endpoint. Use the Backstage proxy so the browser does not call the ScaleOps API directly

  1. Add a proxy entry in app-config.yaml
Copy
# app-config.yaml
proxy:
  '/scaleops':
    target: ${SCALEOPS_BASE_URL}
    headers:
      Authorization: Bearer ${SCALEOPS_TOKEN}
  1. Set environment variables for local development
Copy
# .env
SCALEOPS_BASE_URL=https://your-scaleops-api.example.com
SCALEOPS_TOKEN=your scaleops api token
  1. In the frontend, call the proxy path when you need to query ScaleOps. The card component will handle this if it is wired to the proxy path
Copy
// example of fetching through the proxy if you need to customize anything later
import { useApi, configApiRef, fetchApiRef } from '@backstage/core-plugin-api';

const MyHook = () => {
  const fetchApi = useApi(fetchApiRef);
  const { getString } = useApi(configApiRef);

  // backend base url comes from config
  const baseUrl = getString('backend.baseUrl');
  const url = `${baseUrl}/api/proxy/scaleops/v1/some endpoint`;

  const load = async () => {
    const res = await fetchApi.fetch(url);
    const data = await res.json();
    return data;
  };

  return { load };
};

New backend system setup

If your app uses the new backend system

  1. Install the proxy backend plugin
Copy
yarn add --cwd packages/backend @backstage/plugin-proxy-backend
  1. Register the proxy plugin in packages/backend/src/index.ts
Copy
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';

const backend = createBackend();

// register the proxy backend plugin
backend.add(import('@backstage/plugin-proxy-backend/alpha'));

backend.start();

The proxy reads the config you added in app-config.yaml. No extra code is needed for the ScaleOps route

Old backend system setup

If your app uses the legacy backend

  1. Install the proxy backend plugin
Copy
yarn add --cwd packages/backend @backstage/plugin-proxy-backend
  1. Create a proxy plugin module
Copy
// packages/backend/src/plugins/proxy.ts
import { createRouter } from '@backstage/plugin-proxy-backend';
import { PluginEnvironment } from '../types';

export default async function createPlugin(env: PluginEnvironment) {
  return await createRouter({
    config: env.config,
    logger: env.logger,
    discovery: env.discovery,
    tokenManager: env.tokenManager,
  });
}
  1. Wire it in backend index
Copy
// packages/backend/src/index.ts
import proxy from './plugins/proxy';

// inside the main bootstrap where you create apiRouter
const proxyRouter = await proxy(env);
apiRouter.use('/proxy', proxyRouter);

The proxy will forward requests from the frontend to the ScaleOps target you set in app-config.yaml

Add catalog annotations if your setup needs them

Some teams map catalog entities to ScaleOps by annotations or labels. If your data model needs that, add them to your catalog entity yaml. Replace keys and values to fit your ScaleOps setup

Copy
# catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Service
metadata:
  name: my service
  annotations:
    scaleops.com/project: my project
    scaleops.com/cluster: prod
    scaleops.com/namespace: default
    scaleops.com/workload: my service
spec:
  owner: team a
  system: payments

Run the app

Start the backend and the frontend. Then open a service entity page that has ScaleOps data. You should see the ScaleOps card on the overview tab

Copy
yarn dev

Changelog

This changelog is produced from commits made to the ScaleOps plugin since 9 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

  • Move kyverno kro and crossplane to a backend plugin architecture. You need to install the backend plugin and set permissions. This enables MCP actions and tighter control. #89 merged 2 weeks ago

  • Scaffolder actions now use zod schemas. Update any templates that relied on json schema. #54 merged 4 months ago

  • Kubernetes Ingestor now uses the Kubernetes backend plugin proxy. Make sure the Kubernetes backend plugin is set up with your cluster locator and auth. #82 merged 1 month ago

Features

  • Add MCP Action support in kyverno kro and crossplane. #89 merged 2 weeks ago

  • Add new frontend system support in all frontend plugins. #83 merged 1 month ago

  • Add annotations to override generated component details in the Kubernetes Ingestor
    terasky.backstage.io/name
    terasky.backstage.io/title
    #82 merged 1 month ago

  • Add cluster and kind tags for XR and Claim based components in the Kubernetes Ingestor. #82 merged 1 month ago

Improvements

  • Auto link Crossplane functions and providers from the crossplane contrib org to their source GitHub repo. #82 merged 1 month ago

  • Render YAML manifests in a clearer order for Crossplane resources. #82 merged 1 month ago

  • Use a backend plugin for kyverno kro and crossplane for better security and permission control. #89 merged 2 weeks ago

Bug fixes

  • Fix missing kind and apiVersion for some Kubernetes workload types in the Kubernetes Ingestor. #82 merged 1 month ago

  • Fix scaffolder manifest generation for cluster scoped custom resources that do not use XRDs. #82 merged 1 month ago

Upgrades

  • Upgrade Backstage to 1.43.3. #89 merged 2 weeks ago

  • Upgrade all plugins to Backstage 1.42.5 with dependency bumps. #82 merged 1 month ago

  • Upgrade to Backstage 1.40 and migrate scaffolder actions to zod. #54 merged 4 months ago

  • Routine dependency updates for plugins. #19 merged 8 months ago
    More dependency updates. #14 merged 9 months ago

Set up Backstage in minutes with Roadie