Kubernetes
Kubernetes Cost Allocation: Every Way to Get Per-Namespace Costs That Match Your Invoice
Every working solution for Kubernetes cost allocation — the native options on EKS, GKE, and AKS, plus the OpenCost path for everything else, down to the method that matches your invoice.
·
Costory team
·
8 min read
Kubernetes cost allocation is the process of splitting what your cluster costs — nodes, disks, load balancers — down to the workloads, namespaces, and teams that consumed them. Your cloud bill shows nodes; your cluster runs workloads — that’s the core problem behind kubernetes cost visibility. For years, bridging it meant building your own agent, your own pricing table, and your own reconciliation job. That’s no longer true: today there are many easy solutions for Kubernetes cost allocation, and if you’re on a managed cluster, your provider already has one built in. This article covers the two paths that exist: each cloud’s native option, and the provider-agnostic open-source route for everything else, with the commands to enable each. Commercial SaaS FinOps tools are out of scope: this is about what the providers give you natively, plus the open-source route. And if you want the kubernetes cost per namespace numbers for a whole fleet rather than one cluster, most of these options get you there too.
Managed clusters: the native options
AWS EKS | GCP GKE | Azure AKS | |
|---|---|---|---|
Solution | Split cost allocation data | Built-in cost allocation | Cost analysis add-on (OpenCost-based) |
How to enable | Billing and Cost Management console, management account level, with an hourly CUR — guide | gcloud container clusters update CLUSTER_NAME --enable-cost-allocation | az aks update --name CLUSTER_NAME --resource-group RG --enable-cost-analysis |
What you get | Per-pod cost lines in your CUR, tagged with pod labels (user_<label>) | Cluster / namespace / label cost breakdown in the BigQuery billing export, discounts included | Cost views by namespace in the Azure portal, reconciled with your invoice. Not in the cost export — the CSV export path is the next section |
Allocation basis | Each node’s hourly cost split across pods in proportion to their resource requests vs. the node’s capacity (resource-requests mode) | Pod resource requests, not actual consumption | OpenCost agent data (max(request, usage) per its spec), reconciled with the invoice |
Label limits | 50 labels per pod, alphabetically sorted; tag keys ≤ 128 chars, values ≤ 256; activation at the payer level with 24h delays | 50 labels per pod, dropped beyond | n/a (portal views) |
Constraints | Pods without CPU/memory requests get no split-cost data | Up to 3 days for data to appear, no backfill; some fees land in goog-k8s-unsupported-sku | Standard/Premium tier only (no downgrade to Free while enabled); EA and MCA offer types only; ~7,000 containers per cluster; up to 24h for data collection |
Additional feature fee | None (part of your CUR) | None — but the larger BigQuery export increases storage and query costs | None beyond the Standard/Premium tier fee — the agent itself consumes some cluster resources |
Docs | Split cost allocation data · Kubernetes labels for cost allocation |
One caveat for multi-cloud teams: every provider renames labels in its billing export — user_<label> on AWS, k8s-namespace and k8s-label/<key> on GCP. Same label, two different names in two different bills; keep this in mind if you plan to merge clouds later. The labels themselves are plain Kubernetes labels on your pods:
One thing none of the managed options give you: per-pod network egress. The billing exports above don’t attribute outgoing traffic to workloads or namespaces. Two tools estimate it: OpenCost (open source) attributes egress from network traffic metrics and exports a networkCost figure alongside CPU and memory, and Kubecost (its commercial sibling, a paid product) does the same. The estimate is why the OpenCost export in a later section keeps a networkCost column.
Getting AKS costs out: the CSV export
To get that data out of the portal, Microsoft publishes a self-managed export example: a CronJob inside each cluster that reads the cost analysis agent daily and writes a per-namespace, per-pod results.csv to Azure Blob Storage — one prefix per cluster so several clusters share a container. Run it in export-only mode if your cost export already exists (say, created with Terraform); the README covers deployment. Running the job from outside the cluster is possible too, but it needs private connectivity to the cost analysis agent — extra setup the in-cluster CronJob avoids. Azure was expected to ship this natively in Cost Management exports over a year ago; the GitHub issues tracking it (#4336, #4417) haven’t moved — we hope they export it next to costs soon.
Self-managed clusters and every other cloud: OpenCost + Parquet
EKS, GKE, and AKS cover you if you’re on their clouds. For self-managed clusters — or managed clusters on providers like IBM Cloud or Scaleway — the answer is OpenCost, the open-source cost monitoring project (CNCF Incubating), plus an export. Install it with Helm (it expects an existing Prometheus installation — OpenCost: installation):
OpenCost’s allocation model computes which workload, in which namespace, consumed which share of which node, hour by hour — charging each container the larger of its requested or actually used resources (max(request, usage) per the OpenCost spec). To use those numbers anywhere outside OpenCost’s UI, you export them, and the format matters:
CSV export (Microsoft-contributed — OpenCost: CSV export): set EXPORT_CSV_FILE to a local path, an Azure Blob URL, an S3 bucket, or a GCS path, and OpenCost appends daily-aggregated rows automatically. It works — but the node information is not available in this export, which limits how precisely you can reconcile it against your bill.
Parquet export (preferred — OpenCost: Parquet exporter): the opencost-parquet-exporter connects to OpenCost’s API and writes a daily export with hourly records — everything you need to join against billing data:
The key settings:
Set OPENCOST_PARQUET_INCLUDE_IDLE=true to get the idleCost field — what each node cost with no workload claiming it. OPENCOST_PARQUET_IDLE_BY_NODE=true splits idle cost per node instead of sharing it.
One caveat: OpenCost’s costCpu and costMemory figures use public on-demand price lists where a connector exists, or manually configured prices where it doesn’t (IBM Cloud, Scaleway). That’s list pricing by default — OpenCost can ingest some spot feeds (e.g. AWS Spot Data Feed), but not your negotiated discounts or reservations. The fix is the next section: ignore OpenCost’s prices, apply its per-node allocation shares to your real invoice costs. That works on any provider, since it only needs the node’s resource ID to match OpenCost’s node_name column.
Exact cost per machine: matching the invoice
OpenCost needs a price per CPU and per GB of RAM for each machine type to produce those figures. It cannot natively read every cost export that exists — providers format billing data differently, apply discounts at the line level, and identify machines in ways that don’t match any price list. On IBM Cloud, computing the exact cost of each resource ID with discounts applied is hard for exactly that reason.
The exact method — and the one that works on every provider — inverts the problem. Don’t feed OpenCost prices. Feed it your invoice:
Take the real machine price from the bill. Match each node to its billing line item by resource ID — cost exports typically identify machines by resource ID, which is what makes the match precise. On IBM Cloud, the bill names nodes like d4u2gf6b0bqj87rqo1i0_kube-d4u2gf6b0bqj87rqo1i0-common01tca-zone1-00000541 — the same string that appears in OpenCost’s node_name column, which is what makes the match possible.
Use OpenCost only for the split. OpenCost’s exported allocation metrics (CPU and memory hours per pod per node, per hour) tell you each workload’s share of each node-hour — you compute the share from the allocation amounts, so OpenCost’s own price configuration never enters the result.
Apply the proportional split. Real node cost × workload’s share of the node = real workload cost. Discounts, reservations, spot pricing — everything in the invoice is already in the price, so the node cost is exact; the split follows OpenCost’s allocation model.
No price configuration needed, and it carries over to IBM Cloud, Scaleway, or any provider — the one precondition is that the resource ID in the bill matches the node (match hour by hour against the historical export, not the live cluster). (For namespace and label schemes that survive a customer dispute, see kubernetes cost per customer.)
Reallocation strategies: namespace vs node-based
Once you have usage data, one policy decision shapes every number: how idle cost gets reallocated to workloads. There are two working strategies, and the right one depends on your scheduling:
Strategy | How it works | Best for |
|---|---|---|
Namespace reallocation | Calculates each namespace’s per-hour resource requests and reallocates idle cost proportionally. | Most clusters with standard scheduling. |
Node-based reallocation | Calculates per-hour resource requests per pod on each individual node. A pod alone on a node bears its full cost for the period it’s the sole occupant. | Clusters relying on affinity rules — node pools, zones — where a single pod may trigger a new node. Trade-off: more variance in waste over time. |
The node-pool case illustrates the difference. Say a single pod on an affinity-ruled GPU node pool triggers a new, mostly-idle node. With namespace reallocation, that node’s idle cost is spread proportionally across all namespaces — every team chips in for a node one workload forced. Node-based reallocation charges the idle to the pod that occupied the node, matching what actually happened on the invoice, at the price of noisier idle ratios over time.
From exports to numbers your teams trust
At this point the data exists — every option above gets per-namespace costs out of the cluster and next to your billing data. The complex part is what happens next: four export formats, labels named differently per provider (user_<label> on AWS, k8s-label/ on GCP), and costs that only mean something once they’re reconciled with the invoice.
That’s what Costory does natively: it ingests all four sources, matches each resource ID against your real cost exports, and homogenizes the labels into a single scheme. On top of that, Dimensions handle the allocation policy: merge equivalent labels across clouds, allocate untagged spend, and split shared costs across teams. Costs are delivered as automated reports in Slack or Teams, and a FinOps agent (MCP) lets you query the numbers directly from Claude or Cursor.
FAQ
How do I get Kubernetes costs per namespace?
Depends on where the cluster runs: EKS has split cost allocation data in the CUR, GKE has built-in cost allocation into BigQuery, AKS has the cost analysis add-on, and anything else can run OpenCost. The comparison table above has the enable commands.
Does GKE cost allocation include discounts?
Yes — discounts are propagated to the BigQuery breakdown. Some fee-based costs don’t break down and land in goog-k8s-unsupported-sku instead (GKE docs).
Why isn’t my AKS cost data in my Azure cost export?
The cost analysis add-on’s data is viewable in the portal but not part of native cost exports. Microsoft’s self-managed export example writes a daily CSV to Blob Storage; native export support is still open (#4336).
What’s the difference between OpenCost’s CSV and Parquet export?
The CSV export aggregates daily but doesn’t include node information; the Parquet exporter writes hourly records with node_name, which is what you need to reconcile costs against your invoice.
How do I include idle cost in OpenCost exports?
Set OPENCOST_PARQUET_INCLUDE_IDLE=true on the Parquet exporter; OPENCOST_PARQUET_IDLE_BY_NODE=true splits idle cost per node instead of sharing it (exporter README).
Summary
If you run EKS, enable split cost allocation and activate your pod labels as tags at the payer level.
If you run GKE, enable cost allocation — the breakdown lands in BigQuery with discounts included.
If you run AKS, enable the cost analysis add-on and export CSVs yourself until Azure ships it natively.
If you run anything else, deploy OpenCost, export Parquet with idle costs, and split your real invoice by resource ID.
Whichever you run, Costory ingests all four and homogenizes them into one scheme — plans start at $250 per year and support up to $10M of annual cloud spend.

Put this into practice with your own cost data
30-day free trial. Plans from €250/month, billed annually. We recommend Team (€600/month) once more than one team uses Costory.
First optimization recommendation
The next day
ROI
Pays for itself with the first recommendation you act on
Setup
Under 30 minutes
Data residency
EU
Access
Read-only on your cloud
