What the cluster bill tells you — and what it does not
Your AWS bill for an EKS cluster shows a line item for the managed control plane and separate line items for the EC2 instances that make up your node groups. Your GKE bill shows Compute Engine VM charges for the node pool instances and a control plane fee. What neither bill shows is how those node costs are distributed across the namespaces, deployments, and teams running workloads inside the cluster.
A cluster running 40 nodes across three node pools, with eight engineering teams deploying services, might have a monthly node cost of $180,000. Without namespace-level attribution, that entire cost appears as a single infrastructure line item. Platform engineering owns the cluster. Finance sees the bill. No product engineering team has any visibility into how much of that $180K their services are responsible for.
The pod resource request model
The standard approach to Kubernetes cost attribution uses pod resource requests — the CPU and memory values declared in each pod's container spec — as the basis for proportional node cost allocation. Each node's hourly cost (derived from the EC2 or GCE instance type price) is distributed across the pods scheduled to that node, in proportion to their declared resource requests.
This is a request-based model, not a consumption-based model. A pod that requests 2 vCPUs and 4 GiB memory is allocated the same cost share whether it is actively using 1.8 vCPUs or 0.2 vCPUs. The request-based approach is defensible because Kubernetes schedules nodes based on requests, not actual consumption — the node must be provisioned to satisfy the declared request regardless of whether the workload uses the full requested capacity.
Request-based attribution has one important implication: it makes over-provisioned pods visible as cost-efficiency problems. A pod that requests 4 vCPUs but consistently uses less than 0.5 vCPUs is carrying a 4× cost attribution against a fraction of the value it consumes. That over-provisioning is the team's to fix — and surfacing it per namespace is the mechanism that motivates right-sizing requests as a first-class engineering task rather than an afterthought.
Namespace attribution and the shared namespace problem
Kubernetes namespaces are the natural attribution boundary in most multi-team clusters. A team that owns the payments namespace owns the cost of every pod running in that namespace. This model works cleanly when team-to-namespace ownership is one-to-one, which it typically is in clusters that followed a consistent namespace design at cluster creation time.
Shared namespaces are the attribution gap. The monitoring namespace, the logging namespace, the ingress-nginx namespace — these namespaces contain infrastructure that serves all teams but is owned by platform engineering. The cost of pods in shared namespaces cannot be attributed to a product team without a distribution methodology.
A practical approach is to treat shared infrastructure namespaces as shared service costs and distribute them proportionally to the product namespaces, using the product namespace's fraction of total attributed cluster cost as the distribution weight. This is not perfectly precise, but it eliminates the scenario where platform engineering appears to own 30% of cluster costs that are actually a shared infrastructure tax on all product teams.
EKS vs. GKE attribution differences
EKS attribution requires joining two data sources: the EC2 instance-level billing from the CUR (identifying which instance IDs are in which node group) and the Kubernetes API node labels (which tell you which pods are scheduled on which nodes). For clusters using managed node groups, the node group name appears as an EC2 Auto Scaling Group tag, making the node group to billing line item join straightforward. For clusters using self-managed node groups or Karpenter, the join requires reading the node topology.kubernetes.io/region and eks.amazonaws.com/nodegroup labels from the Kubernetes API.
GKE attribution is somewhat simpler in structure because GKE Node Pools appear as distinct Compute Engine Managed Instance Groups in the billing export, with the cluster name and node pool name as resource labels. The node pool to pod attribution still requires the Kubernetes API to get the pod-to-node scheduling, but the billing-side join is more direct. The GKE usage metering feature (if enabled) provides per-namespace resource consumption data directly in the billing export, though it uses actual consumption rather than resource requests, which produces different attribution numbers from the request-based model.
What to do about idle cluster capacity
Node capacity that is not requested by any pod is unattributed overhead — the cost of running the cluster at a capacity level above what current workloads actually require. This idle capacity is often 15–30% of total node cost in clusters that use static node pools without cluster autoscaler, or in autoscaled clusters that have high minimum node counts to satisfy application warm-up requirements.
Idle cluster overhead should be reported separately from workload attribution, not distributed across namespaces as if it were a shared service cost. Distributing idle node cost across teams creates the wrong incentive — it implies teams should request more pod capacity to reduce their per-unit attribution, when the correct action is for platform engineering to right-size the node pool minimums or implement cluster autoscaler with tighter bin-packing configuration.
The attribution model should show each team: their attributed workload cost (based on pod requests), their share of shared infrastructure namespace cost, and the cluster-wide idle capacity as a separate line item that platform engineering owns and is responsible for optimizing.