The FinOps Foundation's maturity model (Crawl, Walk, Run) was designed for cloud resources where cost attribution is straightforward. Tag an EC2 instance, see what it costs, assign it to a team. Kubernetes breaks that assumption at every level.
A single node can run 40 pods from 12 teams. Resource requests routinely exceed actual usage several times over. And in a CNCF microsurvey, 38% of respondents had no Kubernetes cost monitoring in place at all.
The maturity model still applies. But every capability (visibility, allocation, optimization, governance) looks different when your infrastructure is shared, ephemeral, and abstracted away from the cloud bill.
This post maps the Crawl-Walk-Run framework to five concrete dimensions of Kubernetes cost management, with specific indicators, sourced numbers and practical tooling at each stage.
The Three Stages
The FinOps Foundation defines maturity per capability. You might be at "Run" for cost visibility but "Crawl" for rate optimization. That's expected.
| Stage | What It Means | Key Indicator |
|---|---|---|
| Crawl | Basic reporting, reactive. KPIs defined but not consistently met. Plans to address obvious waste. | ≥70% of spend attributed to a known owner, <20% forecast variance |
| Walk | Processes followed org-wide. Automation covers most cases. Edge cases identified and deliberately deferred. | ≥85% attribution, <10% forecast variance |
| Run | Automation is the default. Edge cases actively addressed. Very high KPIs across the board. | >90% attribution, <5% forecast variance |
The key indicators are the example KPIs from the Foundation's maturity model.
The Foundation organizes these capabilities across four domains: Understand Usage & Cost, Quantify Business Value, Optimize Usage & Cost, and Manage the FinOps Practice. That's 22 capabilities.
For Kubernetes, three of these map cleanly. Quantify Business Value is where most teams get stuck: cost per API request or cost per customer requires cost attribution, and cost attribution is exactly what Kubernetes makes hard.
Why Kubernetes Makes FinOps Harder
Kubernetes eliminates that 1:1 relationship between a billing entity and a workload.
The FinOps Foundation's own Container Costs working group states it plainly: "In the containerized world, traditional FinOps cost allocation doesn't work. You can't simply allocate the cost of a resource to a tag or label, because resources may be running multiple containers, with each supporting a different application."
Three things make this structurally different from VM-level FinOps.
Shared infrastructure. A typical production node runs pods from multiple teams. One line item on your bill, twelve teams using it. Attributing that cost to individual workloads requires knowing each pod's resource requests (or actual usage), the node's total capacity and some method for handling shared overhead: control plane fees, monitoring agents, DaemonSets, ingress controllers. There's no standard way to split these.
The request-usage gap. Kubernetes schedules pods based on resource requests, not actual usage. A pod requesting 1 CPU but using 200m still reserves the full core. Cast AI's 2025 benchmark across more than 2,100 organizations found average CPU utilization of 10% and memory utilization of 23%.
Ephemeral workloads. Containers don't stick around. The scheduler moves pods across instance types and availability zones, and autoscalers add and remove nodes through the day. Good luck tracking that with a monthly billing spreadsheet.
Put together, the attribution problem looks like this:
- The cloud bill shows what you pay for: instances, volumes and data transfer.
- The Kubernetes layer packs pods from many teams onto shared nodes, where requests rarely match usage.
- Namespaces, labels and workloads (Deployments, StatefulSets, CronJobs) are where teams want the cost to land.
- Shared costs (control plane, monitoring, ingress, service mesh) have to be split across teams, and there is no standard method for it.
Five Dimensions of Kubernetes FinOps Maturity
The Foundation's 22 capabilities are a useful reference, but they were written for all of cloud, not just Kubernetes. We've found it more practical to assess maturity across five dimensions that map directly to the engineering decisions you actually make day-to-day.
| Dimension | Crawl | Walk | Run |
|---|---|---|---|
| Resource Requests | Defaults or missing. | VPA in recommendation mode, regular review. | VPA auto-apply, admission webhooks enforce. |
| Cost Visibility | Account-level cloud bill only. | Namespace-level attribution, showback. | Real-time dashboards, anomaly detection. |
| Rate Optimization | All on-demand, little or no spot. | Savings Plans + spot for stateless. | Spot by default, ARM adoption, Karpenter. |
| Autoscaling | Static node pools, no scale-down. | HPA + Cluster Autoscaler, non-prod scheduled. | Karpenter consolidation, >60% utilization. |
| Governance | No cost ownership. | Team budgets, monthly reviews. | Policy-as-code, cost as engineering metric. |
Each of these has a different effort-to-reward curve. Resource requests and autoscaling tend to deliver the biggest dollar savings with the least organizational friction. Governance and cost visibility need buy-in from multiple teams, but they create the foundation for everything else. Rate optimization is pure math: configure the tools, let them work.
Let's go through each one.
1. Resource Requests and Limits
This is where most of the money is. If your pods request several times what they use (at 10% average CPU utilization, most do), right-sizing saves more than any rate optimization or commitment discount will.
| Stage | What It Looks Like |
|---|---|
| Crawl | Most pods have no requests, or copy-pasted defaults (resources: {requests: {cpu: "1", memory: "2Gi"}} everywhere). Teams set values high "just in case." No VPA. |
| Walk | VPA running in recommendation mode. Tools like Goldilocks surface per-workload suggestions. Teams review recommendations and apply them on a regular cadence, monthly or per-sprint. Requests set on >80% of workloads. QoS classes understood and intentional. |
| Run | VPA auto-applying recommendations with safety bounds. Admission webhooks reject pods without resource requests. Continuous right-sizing loop with guardrails: never drop below p99 usage, respect PDB constraints. Result: one team cut CPU requests 48% and memory 39% across 1,200 pods without a single OOM kill. |
Here's the math. A single m5.xlarge on-demand costs $0.192/hour, or $1,682/year. If the pods on that node request its full 4 vCPUs and 16 GiB but actually use 1 vCPU and 4 GiB, three-quarters of the spend is waste. Across a 50-node cluster, that's $63,000/year in CPU nobody uses.
Why does this happen? Teams set requests equal to limits and limits equal to "something big enough." Nobody wants to get paged at 3 AM because a pod got OOM-killed, so they round up. Totally reasonable instinct. But the cost of that safety margin compounds across every workload, and it's almost always way bigger than anyone expects.
2. Cost Visibility and Allocation
You can't fix what you can't see, and most teams can't see much. In the CNCF FinOps microsurvey, 40% of respondents estimated their Kubernetes costs, 38% had no monitoring in place, and just under a fifth (19%) had accurate cost information.
| Stage | What It Looks Like |
|---|---|
| Crawl | Cloud bill viewed at the account level. Kubernetes costs are a black box. You know what your EKS cluster costs in total, but not how that breaks down by team, namespace, or workload. Cost questions answered with spreadsheets and guesswork. |
| Walk | Cost monitoring deployed (OpenCost, Kubecost, or a vendor solution). Costs allocated by namespace and label. Showback reports sent to engineering teams monthly or weekly. ≥85% of spend attributed to a known owner. Shared costs (control plane, monitoring, DaemonSets) distributed by a documented formula. |
| Run | Real-time cost dashboards per team and service. Chargeback integrated with finance systems. Unit economics tracked: cost per API request, cost per customer, cost per transaction. Anomaly detection fires alerts when a workload's cost deviates from its 30-day baseline. >90% of spend attributed. |
The Crawl-to-Walk jump is the single highest-impact move here. Going from "we have no idea" to "here's what each team spends" tends to surface the obvious waste first: forgotten staging environments, oversized dev clusters, deployments that haven't served a request in months.
<!-- callout:note -->Cost allocation is often more political than technical. "Should we charge Team A for the shared Prometheus stack they didn't ask for?" has no correct answer. Pick a method (proportional to requests, proportional to usage, even split, or full absorption by the platform team), document it, and move on. Debating the perfect allocation model is a form of procrastination that delays real optimization.
3. Rate and Commitment Optimization
This dimension covers what you pay per unit of compute: on-demand pricing vs. Savings Plans vs. Reserved Instances vs. Spot.
| Stage | What It Looks Like |
|---|---|
| Crawl | Everything runs on-demand. No Savings Plans or Reserved Instances. Little or no spot. Datadog found that only 29% of organizations cover more than half their eligible spend with commitment discounts. |
| Walk | Compute Savings Plans covering baseline steady-state usage. Spot instances for fault-tolerant workloads (batch jobs, stateless services with multiple replicas). Instance type modernization, migrating from previous-gen (m5) to current-gen (m7g) for better price-performance. |
| Run | Spot for most eligible workloads, with automated fallback to on-demand. Savings Plans sized to cover the minimum sustained floor, not the average. Graviton/ARM adoption where workloads support it. Datadog reports that organizations using Arm-based instances spend 18% of their EC2 compute on them. Karpenter selecting optimal instance types and purchase options automatically. |
Most teams assume spot is too risky. "What if my pods get terminated?" Fair concern, but Kubernetes already handles rescheduling. Add PodDisruptionBudgets, graceful shutdown handlers, and multiple replicas, and most stateless workloads survive spot interruptions without anyone noticing. The financial services team that saved £1.2M annually ran 78% of their workloads on spot across 28 EKS clusters. Financial transactions. On spot. It works.
Here's a detail most teams miss: commitment discounts and spot aren't mutually exclusive. Savings Plans cover your baseline (the compute you'll use no matter what). Spot covers the variable portion on top. The mistake teams make at Crawl is buying Savings Plans based on average usage instead of minimum sustained usage, and then wasting money when demand dips below the commitment.
4. Autoscaling and Right-Sizing
This is where Kubernetes actually has an advantage over traditional infrastructure. The primitives are all there: HPA, VPA, Cluster Autoscaler, Karpenter. The hard part is configuring them for cost efficiency, not just performance. Those are two different objectives, and they often pull in opposite directions.
| Stage | What It Looks Like |
|---|---|
| Crawl | Static node pools sized for peak traffic. Cluster Autoscaler installed with default settings, or not installed at all. Non-production environments run 24/7 at production scale. Off-peak cluster utilization below 15%. |
| Walk | HPA configured for key workloads with CPU/memory targets. Cluster Autoscaler tuned (scale-down delay, utilization threshold). Non-prod environments scheduled to scale down outside business hours. Node utilization improved to 30-50%. |
| Run | Karpenter replacing Cluster Autoscaler, with consolidation actively migrating pods to pack nodes tighter and terminating underutilized nodes. HPA and VPA coordinated. Workload-aware bin-packing. Node utilization above 60%. Result: a SaaS company cut from $52K/month to $23K/month, a 56% reduction. |
A simplified Karpenter NodePool that demonstrates cost-aware provisioning:
1apiVersion: karpenter.sh/v1
2kind: NodePool
3metadata:
4 name: default
5spec:
6 template:
7 spec:
8 requirements:
9 - key: karpenter.sh/capacity-type
10 operator: In
11 values: ["spot", "on-demand"]
12 - key: kubernetes.io/arch
13 operator: In
14 values: ["amd64", "arm64"]
15 - key: karpenter.k8s.aws/instance-category
16 operator: In
17 values: ["m", "c", "r"]
18 - key: karpenter.k8s.aws/instance-generation
19 operator: Gt
20 values: ["5"]
21 nodeClassRef:
22 group: karpenter.k8s.aws
23 kind: EC2NodeClass
24 name: default
25 disruption:
26 consolidationPolicy: WhenEmptyOrUnderutilized
27 consolidateAfter: 30s
28 limits:
29 cpu: "1000"
30 memory: 1000GiThe line that matters is consolidationPolicy: WhenEmptyOrUnderutilized. Karpenter continuously evaluates whether pods could be packed onto fewer nodes. If a more efficient layout exists, it cordons the underutilized node, drains its pods and terminates it, without anyone having to intervene.
<!-- callout:warning -->VPA and HPA can conflict. If HPA scales on CPU utilization and VPA changes the CPU request, HPA's utilization percentage shifts, potentially triggering unexpected scale-up or scale-down events. The safe pattern: use VPA for setting resource requests and HPA with custom metrics (requests per second, queue depth) rather than resource utilization percentages.
5. Governance and Engineering Culture
You can install all the right tools and still waste money if nobody looks at the dashboards. The CNCF microsurvey found that 68% of respondents believe improving team awareness would be the most effective cost reduction strategy. Not better tools. Awareness. The top overspend factors were overprovisioning (70%) and lack of individual/team cost awareness (45%).
| Stage | What It Looks Like |
|---|---|
| Crawl | No cost ownership model. Engineers don't know what their workloads cost. The platform team absorbs the entire cloud bill. Nobody gets paged for a cost spike, only for an outage. |
| Walk | Cost reports shared with engineering leads. ResourceQuotas set per namespace. Cost discussed in sprint retrospectives. Teams have budgets, even informal ones. The platform team runs monthly cost reviews with top-spending teams. |
| Run | Cost is a first-class engineering metric, tracked alongside latency and error rate. Policy-as-code enforcement via OPA or Kyverno: pods without resource requests are rejected at admission. Automated guardrails prevent deploying workloads with requests exceeding 2x their historical p99 usage. |
The Crawl-to-Walk transition here is cultural, not technical. It starts with one dashboard and one conversation: "Team X, your namespace costs $8,400/month. Here's the breakdown. Did you know?"
We've seen that conversation go three ways. Surprise: "we had no idea." Defensiveness: "we need all those resources." Or, most commonly, the sheepish grin: "oh, we forgot to delete that staging deployment three months ago." The sheepish grin is where the quickest wins hide.
The Run Trap
Rob Martin, one of the framework's authors at the FinOps Foundation, says something we agree with: "The goal is not to be at 'Run' maturity in all (or indeed any) of your Capabilities."
He talks about a Global 50 company that deliberately uses a Crawl-level allocation process. Not because they haven't invested. Because their simple approach works well enough, and the engineering effort to build something fancier just isn't worth it for them.
Same thing with Kubernetes. If your cluster is 10 nodes and $3,000/month, building a real-time chargeback system with anomaly detection and policy-as-code guardrails is over-engineering. The return isn't there. Spend your effort on the dimension with the biggest dollar impact (usually resource requests and autoscaling) and leave the rest at Walk or even Crawl.
Think of the maturity model as a map, not a scoreboard.
What Matters More Than Your Score
Here's the number that matters: in Datadog's State of Cloud Costs, 83% of container costs were associated with idle resources. Not misconfigured, not overpriced: idle, reserved for pods that don't use it.
Datadog splits that waste into two buckets, and the split tells you where to point your effort:
| Where container spend goes | Share of container cost | What causes it |
|---|---|---|
| Cluster-level idle | 54% | Too many or too large nodes, static pools sized for peak, no consolidation or scale-down |
| Workload-level idle | 29% | Requests far above actual usage, copy-pasted resource defaults, no right-sizing process |
| Used by running workloads | 17% | The work you actually pay for |
54% is cluster-level waste. Too many nodes, or nodes that are too large. That's the autoscaling problem. Fix it with Karpenter, consolidation, and spot.
29% is workload-level waste. Pods requesting more CPU and memory than they actually use. That's the resource requests problem. Fix it with VPA and real usage data.
If your cluster runs at 10% CPU utilization (that's the industry average right now), moving to even 30% reclaims two-thirds of your wasted spend. That single improvement matters more than perfecting your maturity score across all five dimensions.
So here's the one thing you can do today: measure your request-to-usage ratios per workload, not as cluster-wide averages. The workloads with the widest gap between what they ask for and what they use are your first targets.
If you want those numbers for your own cluster, Kubeadapt computes them for every workload. Install the agent and workload-level cost data shows up as soon as it reports; rightsizing recommendations follow after about a week of usage data. Start free.
Sources
- FinOps Foundation - Maturity Model
- FinOps Foundation - Framework Domains
- FinOps Foundation - Calculating Container Costs
- FinOps Foundation - Maturity Model Insights (Rob Martin)
- Cast AI - 2025 Kubernetes Cost Benchmark
- Cast AI - 2025 Kubernetes Cost Benchmark press release
- Datadog - State of Cloud Costs
- CNCF - Cloud Native FinOps Microsurvey



