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-insights module to serve cost series.
  • Agreement on the cost metric shown: daily spend, month-to-date, or trend against a baseline.
Prerequisites for the cost tab A populated catalog, tagged cost data, a cost endpoint, and an agreed metric enable a per-entity cost tab. populated catalog tagged cost data cost endpoint agreed metric Cost visible to owners
The mapping from cost data to entity is the whole game — without a reliable tag, spend cannot be attributed to a service at all.

Exact Configuration

  1. 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
    
  2. 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}]
    });
    
  3. 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>
    );
    
Cost data reaches the tab An allocation tag maps billing data to the entity, the backend serves a series, and the tab renders the trend. billing data by tag allocation tag → entity cost endpoint 30-day series Cost tab trend chart
Placing cost next to the service — and next to its recent deploys — is what turns an abstract number into an actionable one.

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
Cost tab validation checks The entity carries a tag, the endpoint returns a series, and the tab route resolves. Tag present on the entity Series returns 30 points Tab resolves route 200
An empty series with a valid tag points at the allocation mapping, not the tab — check that billing data actually carries that tag.

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
Cost tab pitfalls by class Missing and misattributed data are tagging issues; stale numbers and currency confusion are presentation issues. Tagging no data misattributed → per-service tags Presentation stale numbers currency confusion → label freshness + unit
Misattributed cost is worse than none — a shared tag charges one team for another's spend, so insist on per-service allocation.

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.