Audit resource requests, configure HPA stabilization windows, deploy Karpenter spot node pools, enforce ResourceQuotas and LimitRanges, install Kubecost, and run a monthly cost review cycle. This checklist takes a cluster from unchecked spending to lean, observable infrastructure in one sprint.
Running Kubernetes in production without cost guardrails is like leaving every light on in a skyscraper overnight. According to the CNCF 2024 FinOps Survey, 65% of organizations overspend on Kubernetes by 30-50% because nobody audits resource requests after initial deployment. A Datadog 2025 container report found that the median cluster runs at only 34% CPU utilization, meaning two-thirds of purchased compute is wasted. This checklist gives you concrete, copy-paste commands and YAML manifests to fix that.
---
1. Audit Resource Requests and Limits
The single biggest source of Kubernetes waste is over-requested CPU and memory. Developers set requests high during the initial deployment panic and never revisit them. According to Sysdig's 2024 Container Security Report, over 60% of containers request 2x or more resources than they actually consume.
Find overprovisioned pods
<h1>Show pods where actual CPU usage is less than 20% of requested</h1>
kubectl top pods -A --no-headers | awk '{
split($3, cpu, "m");
if (cpu[1] < 50) print $1, $2, $3, "(low CPU)"
}'
Compare requests vs actual usage
<h1>Install metrics-server if not present</h1>
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
<h1>Per-namespace resource consumption summary</h1>
kubectl resource-capacity --sort cpu.request --util
<h1>Find pods with no resource requests at all (unbounded risk)</h1>
kubectl get pods -A -o json | jq -r '
.items[] |
select(.spec.containers[].resources.requests == null) |
.metadata.namespace + "/" + .metadata.name
'
<h1>Export a CSV of all requests vs limits for spreadsheet analysis</h1>
kubectl get pods -A -o json | jq -r '
["Namespace","Pod","Container","CPU Req","CPU Lim","Mem Req","Mem Lim"],
(.items[]
.metadata as $m
.spec.containers[] |
[$m.namespace, $m.name, .name,
(.resources.requests.cpu // "none"),
(.resources.limits.cpu // "none"),
(.resources.requests.memory // "none"),
(.resources.limits.memory // "none")])
| @csv
' > resource-audit.csv
Set a monthly calendar reminder to run this audit. Better yet, automate it as a CronJob that posts results to Slack. On Arxentra's managed platform, resource analytics are built into the dashboard so you can spot waste without SSH access.
Resource audit results interpretation
| Finding | Action | Priority |
| CPU request > 3x actual usage | Reduce request to 1.5x p95 usage | High |
| Memory request > 2x actual usage | Reduce request to 1.3x p99 usage | High |
| No requests or limits set | Add immediately, cluster stability risk | Critical |
| Limits equal requests | Consider adding headroom to limits only | Medium |
| CPU throttling detected | Raise limits, keep requests stable | Medium |
According to Google Cloud's 2025 GKE Best Practices report, right-sizing resource requests alone reduces cluster costs by 25-40% without any architecture changes. The key metric is the ratio between your p95 actual usage and your request value, which should stay between 1.2x and 1.8x.
---
2. Tune HPA Thresholds Correctly
The Horizontal Pod Autoscaler is powerful but badly configured in most clusters. The default CPU target of 80% is often wrong for your workload profile. CNCF survey data from 2024 shows that 41% of teams using HPA never modified the default thresholds, leading to either constant scaling thrash or failure to scale under load.
Properly configured HPA with stabilization
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-server
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 2
maxReplicas: 20
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 25
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Percent
value: 100
periodSeconds: 30
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 65
Key tuning decisions explained:
- Scale-down stabilization at 300s prevents thrashing during traffic dips. Without this, a brief drop in traffic causes pod termination, then a small spike causes recreation, wasting both compute and time.
- Scale-up at 30s responds quickly to load spikes. You want asymmetric scaling: fast up, slow down.
- CPU target at 65% leaves headroom for burst traffic without over-provisioning. A 2024 Robusta.dev analysis of 500+ production clusters found that 65% is the optimal CPU target for API workloads, balancing cost and latency.
- Scale-down limited to 25% per minute avoids dropping capacity too fast during load fluctuations.
HPA target selection guide
| Workload Type | Recommended CPU Target | Scale-Down Window | Reasoning |
| Stateless API | 60-70% | 300s | Needs burst headroom |
| Background worker | 75-85% | 600s | Tolerates latency, slow scale-down saves money |
| WebSocket server | 50-60% | 120s | Connection migration is expensive |
| Batch processor | 85-95% | 900s | Maximize utilization, long stabilization |
For teams running multiple services on Arxentra's cloud computing platform, HPA configurations are managed through the dashboard with automatic threshold recommendations based on historical usage patterns.
---
3. Adopt Spot/Preemptible Nodes for Stateless Workloads
Spot instances cost 60-90% less than on-demand across all major clouds. AWS reports that the average spot discount is 70% off on-demand pricing as of 2025. The trade-off is interruption, but stateless workloads handle this gracefully with proper pod disruption budgets. According to Spot.io's 2024 report, organizations using spot instances save an average of $147,000 per year per 100-node cluster.
Karpenter NodePool with spot instances and diversification
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: spot-pool
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
- key: node.kubernetes.io/instance-type
operator: In
values:
- m5.xlarge
- m5a.xlarge
- m6i.xlarge
- m6a.xlarge
- c5.xlarge
- c6i.xlarge
nodeClassRef:
name: default
limits:
cpu: "100"
memory: 400Gi
disruption:
consolidationPolicy: WhenUnderutilized
expireAfter: 720h
Key considerations for spot adoption:
- Diversify instance types across at least 6 types. This reduces interruption frequency by 80% because AWS draws from multiple capacity pools.
- Mix instance families (m5, m6i, c5, c6i) rather than sticking to one generation. This maximizes the chance of finding available capacity.
- Set a PodDisruptionBudget for every stateless workload so Kubernetes can gracefully drain spot nodes before termination.
- Never run databases or stateful workloads on spot. Use on-demand or reserved instances for those.
PodDisruptionBudget for spot-ready workloads
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-server-pdb
spec:
minAvailable: "50%"
selector:
matchLabels:
app: api-server
---
4. Enforce Namespace Quotas and LimitRanges
Without quotas, a single team can consume your entire cluster. ResourceQuotas cap total namespace consumption, while LimitRanges set defaults and bounds for individual pods. These are your financial guardrails at the namespace level.
ResourceQuota for a team namespace
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-backend-quota
namespace: team-backend
spec:
hard:
requests.cpu: "20"
requests.memory: 40Gi
limits.cpu: "40"
limits.memory: 80Gi
persistentvolumeclaims: "10"
services.loadbalancers: "2"
pods: "100"
LimitRange to set defaults and prevent outliers
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: team-backend
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 100m
memory: 128Mi
max:
cpu: "4"
memory: 8Gi
min:
cpu: 50m
memory: 64Mi
The LimitRange is particularly important because it catches pods deployed without any resource specifications. Instead of running unbounded, they get sensible defaults. This single manifest prevents the most common cause of noisy-neighbor problems in multi-tenant clusters.
---
5. Install Kubecost for Real-Time Cost Visibility
You cannot optimize what you cannot measure. Kubecost provides per-namespace, per-deployment, and per-pod cost attribution. The open-source version covers most needs. According to Kubecost's own customer data, teams that deploy Kubecost reduce their cloud spend by an average of 30-50% within the first 90 days simply through visibility.
Install Kubecost via Helm
<h1>Add the Kubecost Helm repository</h1>
helm repo add kubecost https://kubecost.github.io/cost-analyzer/
helm repo update
<h1>Install Kubecost into its own namespace</h1>
helm install kubecost kubecost/cost-analyzer \
--namespace kubecost \
--create-namespace \
--set kubecostToken="your-token-here" \
--set prometheus.server.retention=30d \
--set networkCosts.enabled=true
<h1>Verify installation</h1>
kubectl get pods -n kubecost
<h1>Port-forward to access the dashboard</h1>
kubectl port-forward -n kubecost svc/kubecost-cost-analyzer 9090:9090
Key Kubecost metrics to monitor weekly
| Metric | What It Tells You | Action Threshold |
| Cluster efficiency | Overall utilization vs cost | Below 50% needs attention |
| Namespace cost | Team-level spending | Any namespace above budget |
| Idle cost | Money spent on unused resources | Above 20% of total |
| Network egress cost | Cross-AZ and internet traffic | Spikes above baseline |
| Storage cost | PV and snapshot expenses | Unused PVCs |
Teams using Arxentra's infrastructure management get cost dashboards built in, but Kubecost remains the gold standard for self-managed clusters.
---
6. Run a Monthly Cost Review Cycle
Technology alone does not control costs. You need a recurring process. The most effective teams run a monthly 30-minute cost review with three fixed agenda items.
Monthly cost review agenda
Automation script for monthly report
#!/bin/bash
<h1>Monthly K8s cost audit script</h1>
echo "=== K8s Monthly Cost Report ==="
echo "Date: $(date -u +%Y-%m-%d)"
echo ""
echo "--- Node Utilization ---"
kubectl top nodes
echo ""
echo "--- Namespace Resource Usage ---"
kubectl resource-capacity --sort cpu.request --util --namespace-count
echo ""
echo "--- Pods Without Resource Requests ---"
kubectl get pods -A -o json | jq -r '
[.items[]
select(.spec.containers[].resources.requests == null)]
length
' | xargs -I {} echo "{} pods without requests"
echo ""
echo "--- Spot vs On-Demand Node Distribution ---"
kubectl get nodes -L karpenter.sh/capacity-type -L node.kubernetes.io/instance-type --no-headers | \
awk '{print $6}'
sort uniq -c
sort -rn
Save this script and run it on the first Monday of every month. Pipe the output to a Slack channel for team visibility.
---
Frequently Asked Questions
How much can I realistically save with this checklist?
Most teams see 30-50% reduction in their Kubernetes bill within the first month. The biggest wins come from right-sizing resource requests (Step 1) and adopting spot instances (Step 3). A 2025 FinOps Foundation study found that organizations following a structured cost optimization process save 2.4x more than those doing ad-hoc optimization.
Should I set CPU limits or just requests?
This is debated. Google's official recommendation since 2023 is to set CPU requests but not CPU limits for most workloads. CPU limits cause throttling even when the node has spare capacity. Memory limits should always be set to prevent OOM kills from affecting other pods. Tim Hockin (Kubernetes co-founder) has publicly stated that CPU limits cause more problems than they solve for the majority of workloads.
How do I convince management to invest time in cost optimization?
Run the audit commands from Step 1 and calculate the annual waste. Present it as: "We are paying $X per year for compute we are not using. This checklist will recover Y% of that in one sprint." The CNCF FinOps survey found that the average ROI on Kubernetes cost optimization is 8:1 in the first year.
Is Kubecost worth it for small clusters (under 20 nodes)?
Yes. The open-source version is free and runs on minimal resources (about 0.5 CPU and 1GB RAM). Even on a 5-node cluster, the visibility it provides typically identifies savings that exceed its resource cost by 10x within the first week.
How often should I right-size resource requests?
Monthly for active services, quarterly for stable ones. The Arxentra blog regularly publishes updated benchmarks and tooling recommendations. Automate the audit with the script from Step 6 so you never forget. VPA (Vertical Pod Autoscaler) in recommendation mode can also continuously suggest right-sized values without applying them automatically.
What is the relationship between requests, limits, and QoS classes?
Kubernetes assigns QoS classes based on how you set requests and limits. Guaranteed (requests equal limits) pods are never evicted first. Burstable (requests set, limits higher or unset) pods can use spare capacity but get evicted before Guaranteed pods. BestEffort (no requests or limits) pods are evicted first during node pressure. For cost optimization, most stateless workloads should be Burstable with well-tuned requests.