Adding a Cost Insights Tab to Entities
Cloud cost is usually invisible to the engineers who drive it, surfacing only in a finance dashboard nobody on the team opens. Putting a cost tab on the entity page turns spend into something a service owner sees next to their service, next to the deploy that doubled it. This how-to adds a cost-insights tab to the entity layout, fed by your cloud billing data. It extends Custom UI Components for Portals within Plugin Ecosystem & Custom Extensions.
Prerequisites
- Backstage 1.20+ with a populated catalog and services owned by groups.
- A source of cost data keyed by something you can map to entities — cost-allocation tags, project ids, or labels.
- A backend endpoint or the
@backstage-community/plugin-cost-insightsmodule to serve cost series. - Agreement on the cost metric shown: daily spend, month-to-date, or trend against a baseline.
Exact Configuration
-
Map cost data to entities with a cost annotation carrying the allocation key.
# catalog-info.yaml metadata: name: payments-api annotations: cost.io/allocation-tag: team-payments/payments-api -
Serve a cost series the tab can render, keyed on the allocation tag.
// plugins/cost-backend/src/router.ts router.get('/series', async (req, res) => { const tag = String(req.query.tag); const series = await costStore.dailySpend(tag, { days: 30 }); res.json({ tag, unit: 'USD', series }); // [{date, amount}] }); -
Add the tab to the entity layout so it appears alongside the other entity views.
// packages/app/src/components/catalog/EntityPage.tsx import { EntityCostContent } from '@internal/plugin-cost'; const serviceEntityPage = ( <EntityLayout> <EntityLayout.Route path="/cost" title="Cost"> <EntityCostContent /> </EntityLayout.Route> </EntityLayout> );
Validation
# Requires curl, jq
# 1. The entity carries an allocation tag
curl -s "${PORTAL_URL}/api/catalog/entities/by-name/component/default/payments-api" \
| jq '.metadata.annotations["cost.io/allocation-tag"]'
# Expected: a non-null tag
# 2. The cost endpoint returns a series
curl -s "${PORTAL_URL}/api/cost/series?tag=team-payments/payments-api" | jq '.series | length'
# Expected: ~30 daily points
# 3. The tab route is registered
curl -s "${PORTAL_URL}/catalog/default/component/payments-api/cost" -o /dev/null -w "%{http_code}\n"
# Expected: 200
Edge Cases & Troubleshooting
| Symptom | Root Cause | Resolution |
|---|---|---|
| Tab shows no data | Allocation tag missing or unmatched | Confirm the annotation matches a real billing tag |
| Cost attributed to wrong team | Shared tag across services | Use per-service allocation tags |
| Numbers lag reality | Billing export delayed | Label the tab with the data’s freshness window |
| Untagged spend invisible | Resources without cost tags | Enforce tagging; show an “untagged” bucket |
| Currency confusion | Mixed currencies unlabeled | Return and display an explicit unit |
Frequently Asked Questions
How accurate does the cost data need to be?
Directionally accurate is enough to change behavior. Engineers act on “this doubled after last week’s deploy” long before they need cent-level precision. Show a clear trend with an honest freshness label, and leave the reconciled, to-the-cent figures to finance — the portal’s job is to make cost visible where decisions are made.
Should this gate deploys or just inform?
Inform first. A cost tab that surfaces spend next to the service builds the awareness that changes behavior; hard gates on cost tend to breed workarounds. Once teams are used to seeing and owning their numbers, a soft budget alert is a natural next step — but start by making cost visible, not by blocking on it.