OpenCost logo

Backstage OpenCost Plugin

Created by Matt Ray

OpenCost helps you understand what your Kubernetes costs. It is an open source CNCF project. It breaks spend down by cluster, namespace, workload, and label. You can track trends over time and map costs to teams and services. This makes cost data part of everyday engineering work.

The OpenCost Backstage plugin brings those views into your internal portal. It adds a page that mirrors the familiar OpenCost UI so engineers can see live allocation reports next to their service pages. You can filter by cluster or label, switch time ranges, and compare spend across services. The plugin began as a port of the OpenCost UI and will continue to add data and prebuilt views as the project grows.

This is useful when you want cost awareness without leaving Backstage. Service owners can check the impact of a deploy, catch spend spikes early, and support showback or chargeback. Platform teams can standardize how cost data appears across services. Product teams get clearer conversations about budgets because cost sits next to docs, runbooks, and scorecards. The result is faster feedback on cost and fewer surprises.

Installation Instructions

These instructions apply to self-hosted Backstage only.

Install the frontend package

Run this from your Backstage root

Copy
yarn --cwd packages/app add @backstage-community/plugin-opencost

Add the page route

Edit packages/app/src/App.tsx

Copy
// packages/app/src/App.tsx
import React from 'react';
import { FlatRoutes } from '@backstage/core-app-api';
import { Route } from 'react-router';
import { OpenCostPage } from '@backstage-community/plugin-opencost';

export const AppRoutes = () => (
  <FlatRoutes>
    {/* other routes */}
    <Route path="/opencost" element={<OpenCostPage />} />
  </FlatRoutes>
);

Edit packages/app/src/components/Root/Root.tsx

Copy
// packages/app/src/components/Root/Root.tsx
import React, { PropsWithChildren } from 'react';
import { SidebarPage, Sidebar, SidebarItem } from '@backstage/core-components';
import MoneyIcon from '@material-ui/icons/MonetizationOn';

export const Root = ({ children }: PropsWithChildren<{}>) => (
  <SidebarPage>
    <Sidebar>
      {/* your other items */}
      <SidebarItem icon={MoneyIcon} to="opencost" text="OpenCost" />
    </Sidebar>
    {children}
  </SidebarPage>
);

Configure OpenCost direct access

If your OpenCost API is reachable from the browser, add this to app-config.yaml

Copy
opencost:
  baseUrl: http://localhost:9003

If you run OpenCost in a cluster without ingress, you can port forward while testing

Copy
kubectl -n opencost port-forward deployment/opencost 9003

Configure using the Backstage proxy

If the OpenCost service is private, use the proxy. Add this to app-config.yaml

Copy
proxy:
  endpoints:
    /opencost: http://opencost-svc.opencost:9003
---
opencost:
  baseUrl: http://your-backstage-instance:7007/api/proxy/opencost

You must enable the proxy in your backend. Pick the section that matches your backend setup.

Enable the proxy in the new backend system

Add the proxy backend package

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

Register the module in packages/backend/src/index.ts

Copy
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';

const backend = createBackend();

// add this line to enable the proxy backend
backend.add(import('@backstage/plugin-proxy-backend'));

backend.start();

Enable the proxy in the legacy backend system

Add the proxy package

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

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

Copy
// packages/backend/src/plugins/proxy.ts
import { createRouter } from '@backstage/plugin-proxy';
import { PluginEnvironment } from '../types';

export default async function createPlugin(env: PluginEnvironment) {
  return await createRouter({
    logger: env.logger,
    config: env.config,
    discovery: env.discovery,
    tokenManager: env.tokenManager,
  });
}

Wire it up in packages/backend/src/index.ts so it mounts at api proxy

Copy
// packages/backend/src/index.ts
import proxy from './plugins/proxy';

// inside your main setup where app is created
app.use('/api/proxy', await proxy(env));

env comes from your existing backend bootstrap code. Use the same env you pass to your other backend plugins.

Changelog

This changelog is produced from commits made to the OpenCost plugin since a year 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.

Maintenance

  • Remove unused dev dependency canvas #3565 merged 6 months ago
  • Reduce false positives in unused code reports for this plugin. Update @backstage/repo-tools to 0.13.0 and use a workspace config #3018 merged 7 months ago

Set up Backstage in minutes with Roadie