/

/

Kubernetes Cost Allocation: Every Way to Get Per-Namespace Costs That Match Your Invoice

Kubernetes Cost Allocation: Every Way to Get Per-Namespace Costs That Match Your Invoice

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

Cluster costs breakdown

AKS cost analysis

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:

apiVersion: v1
kind: Pod
metadata:
  name: payments-api
  labels:
    cost-center

apiVersion: v1
kind: Pod
metadata:
  name: payments-api
  labels:
    cost-center

apiVersion: v1
kind: Pod
metadata:
  name: payments-api
  labels:
    cost-center

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):

helm repo add opencost-charts https://opencost.github.io/opencost-helm-chart
helm repo update
helm install opencost opencost-charts/opencost \
    --namespace opencost --create-namespace
helm repo add opencost-charts https://opencost.github.io/opencost-helm-chart
helm repo update
helm install opencost opencost-charts/opencost \
    --namespace opencost --create-namespace
helm repo add opencost-charts https://opencost.github.io/opencost-helm-chart
helm repo update
helm install opencost opencost-charts/opencost \
    --namespace opencost --create-namespace

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:

OPENCOST_PARQUET_INCLUDE_IDLE=true   # export the idle cost field
OPENCOST_PARQUET_IDLE_BY_NODE=false  # per-node idle allocations if true
OPENCOST_PARQUET_STEP=1h             # hourly steps — matches billing exports
OPENCOST_PARQUET_S3_BUCKET

OPENCOST_PARQUET_INCLUDE_IDLE=true   # export the idle cost field
OPENCOST_PARQUET_IDLE_BY_NODE=false  # per-node idle allocations if true
OPENCOST_PARQUET_STEP=1h             # hourly steps — matches billing exports
OPENCOST_PARQUET_S3_BUCKET

OPENCOST_PARQUET_INCLUDE_IDLE=true   # export the idle cost field
OPENCOST_PARQUET_IDLE_BY_NODE=false  # per-node idle allocations if true
OPENCOST_PARQUET_STEP=1h             # hourly steps — matches billing exports
OPENCOST_PARQUET_S3_BUCKET

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:

  1. 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.

  2. 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.

  3. 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