Cost Visibility (OpenCost / Kubecost)
Summary
- OpenCost is the CNCF project that allocates real cloud spend down to namespaces, controllers, and pods; Kubecost is the commercial product built on the same allocation engine.
- Insight: the cloud bill tells you the cluster cost $40k. Allocation tells you which team's over-requested Deployment is $9k of it, and that is the only version anyone acts on.
- Key Concepts: allocation (splitting node cost across pods), showback (reporting cost to a team), chargeback (actually billing it), idle cost (the gap between what pods requested and what the node cost), and the max(request, usage) rule that drives most allocation math.
- When to Use: as soon as more than one team shares a cluster, or as soon as anyone asks "why did the bill go up?"
- Limitations: allocation is an estimate, not an invoice. Shared and idle costs must be assigned by a policy you choose, and no policy is objectively correct.
Recipe
- Ensure Prometheus is running and scraping kube-state-metrics plus cAdvisor, since allocation is computed from those series.
- Install OpenCost with Helm and point it at your Prometheus and your cloud billing export.
- Enforce a namespace and pod labeling convention (
team,env,cost-center) so allocation has something to group by. - Query allocation by namespace and by label, and publish a weekly report per team.
- Once the numbers are trusted, attach them to something: a budget alert, a chargeback line, or an efficiency target.
Working Example
Install OpenCost alongside an existing Prometheus.
helm repo add opencost https://opencost.github.io/opencost-helm-chart
helm repo update
helm upgrade --install opencost opencost/opencost \
--namespace opencost --create-namespace \
--set opencost.prometheus.internal.enabled=false \
--set opencost.prometheus.external.enabled=true \
--set opencost.prometheus.external.url=http://prometheus-server.monitoring.svc:80Query allocation for the last week, grouped by namespace.
kubectl port-forward -n opencost svc/opencost 9003:9003
curl -sG 'http://localhost:9003/allocation/compute' \
--data-urlencode 'window=7d' \
--data-urlencode 'aggregate=namespace' \
--data-urlencode 'accumulate=true' | jq '.data'Group by team label instead, and include idle so the numbers reconcile against the bill.
curl -sG 'http://localhost:9003/allocation/compute' \
--data-urlencode 'window=30d' \
--data-urlencode 'aggregate=label:team' \
--data-urlencode 'includeIdle=true' \
--data-urlencode 'accumulate=true' | jq '.data'Label namespaces so the aggregation has something to group on.
apiVersion: v1
kind: Namespace
metadata:
name: checkout
labels:
team: payments
cost-center: "4412"
env: prodDeep Dive
How allocation works
OpenCost reads each node's hourly price from the cloud provider's pricing API or your billing export.
It then splits that node cost across the pods that ran on it, weighted by each pod's CPU and memory share for the minutes it was scheduled.
Persistent volumes, load balancers, and network costs are attributed separately, then joined onto the same allocation records.
The max(request, usage) rule
A pod's cost share is based on the greater of what it requested and what it used.
That is the honest accounting: a pod requesting 4 CPU and using 0.2 still holds 4 CPU of schedulable capacity that nobody else can have.
This single rule is what turns cost visibility into a right-sizing lever, because over-requesting shows up as spend rather than as a footnote.
Idle cost
Idle is node cost that no pod requested: the headroom, the reserved system resources, and the slack from imperfect bin-packing.
By default idle appears as its own bucket, which is useful for platform teams and confusing for application teams.
shareIdle distributes it proportionally across namespaces, which makes team reports sum to the actual bill and gives teams a stake in cluster packing.
Showback before chargeback
Showback publishes the numbers; chargeback moves real budget.
Start with showback for at least a quarter, because the first reports always surface allocation bugs, missing labels, and mislabeled namespaces.
Charging teams for numbers they do not trust ends the program faster than any technical failure.
OpenCost or Kubecost
OpenCost is the open, Apache-2.0 core: the allocation engine, an API, and a basic UI.
Kubecost adds multi-cluster aggregation, longer retention, saved reports, budget alerts, governance, and support.
The allocation math is the same, so start with OpenCost and upgrade when retention or multi-cluster reporting becomes the bottleneck.
Gotchas
Unlabeled namespaces become __unallocated__. If a third of your spend lands there, no report is credible. Enforce labels with a policy engine before you launch the program.
Allocation will not match the invoice exactly. Discounts, commitments, credits, and shared services all skew it. Reconcile against the billing export and tell people up front that this is a model.
Short Prometheus retention silently caps your window. A 15-day retention makes a 30-day query quietly wrong rather than an error.
Spot price volatility shows as cost noise. A team's spend can move without any change on their side, so compare efficiency ratios, not just dollars.
Cost dashboards nobody owns get ignored. Attach each report to a named person and a recurring meeting, or you have built a very expensive graph.
Charging back idle to app teams before the platform can pack nodes well makes teams pay for a problem they cannot fix. Fix consolidation first.
Alternatives
Cloud-native cost tools (AWS Cost Explorer, GCP Billing, Azure Cost Management) split by account, tag, and service. They are authoritative on the bill but blind to what runs inside a node, so they cannot see namespaces.
Cluster-per-team gets you perfect attribution from the billing account with zero allocation math, at the cost of more clusters, more idle headroom, and more control planes.
Prometheus plus a hand-rolled dashboard works for a single team on uniform nodes: multiply requests by a hardcoded node rate. It breaks the moment your node fleet becomes heterogeneous.
Third-party FinOps platforms correlate Kubernetes with the rest of your cloud footprint. Worth it when Kubernetes is a minority of a large bill.
FAQs
Do I need Prometheus for OpenCost? Yes. Allocation is computed from Prometheus series, and the chart can either install its own or use yours.
Does OpenCost show cost per pod? Yes, aggregated by pod, controller, namespace, label, or node, over any window Prometheus retains.
How accurate is it? Close on compute, weaker on shared and networking costs. Treat it as directional at the pod level and reliable at the namespace level over a month.
What is the difference between showback and chargeback? Showback reports what a team spent. Chargeback moves that amount out of their budget. Showback changes behavior at a fraction of the political cost.
Should idle be shared or shown separately? Show it separately while the platform team owns packing, and share it once teams can influence it through their own requests.
Can it span multiple clusters? OpenCost is per-cluster; you aggregate externally. Multi-cluster rollups are a Kubecost feature.
Related
- Why Kubernetes Costs What It Does
- Right-Sizing Requests & Limits
- Kubernetes Cost Basics
- Karpenter
- Cost Optimization Best Practices
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).