· Updated 2026-08-06

Kubernetes Cost Optimization: Right-Sizing, Spot Nodes, and Autoscaling to Reduce K8s Spend

Kubernetes abstracts away the hardware, but it does not abstract away the bill. Most production clusters bill at 3–5× their actual computational load — the gap between provisioned capacity and utilised capacity is where the waste lives.

The causes are structural: teams over-provision pods to avoid evictions, node pools sit at their maximum size during off-peak hours, and system overhead consumes 20–30% of every node before a single application workload runs. Without active management, the bill grows with cluster size, not with actual work done.

This guide covers the specific tools and techniques that close that gap: right-sizing at the pod and node level, Karpenter for intelligent node provisioning, spot instance node pools, namespace governance, and cost visibility tooling that makes the savings sustainable.


Why Kubernetes Clusters Are Expensive by Default

A fresh cluster before any optimisation typically looks like this:

Layer Overhead
System pods (kube-system) 5–15% of node capacity
Monitoring stack (Prometheus, Grafana) 5–10% per node
Logging agent (Fluentd, Fluent Bit) 2–5% per node
Service mesh (Istio, Linkerd sidecars) 10–20% of pod capacity
Pod request over-provisioning 30–50% of remaining capacity

Before optimisation, it is common for actual workload utilisation to be 20–30% of what the bill reflects.


Cost Visibility First: Kubecost and OpenCost

You cannot optimise what you cannot see at the workload level. Cloud billing shows you node cost; it does not tell you which deployment, which namespace, or which team is responsible for it.

Kubecost (open-source core donated to CNCF as OpenCost) fills this gap. Deployed inside your cluster, it correlates Kubernetes resource consumption with cloud billing data to produce cost-per-workload views:

  • Cost per namespace, deployment, service, and pod
  • CPU and memory efficiency scores per workload
  • Right-sizing recommendations based on actual usage
  • Team-level cost allocation dashboards

Deploy it and run it for 2–4 weeks before taking optimisation actions. The baseline data makes recommendations credible and lets you measure savings precisely.


Right-Sizing Pods: The Foundation

Pod right-sizing is the foundation of all other Kubernetes cost work. Node autoscalers provision capacity based on pod requests — if requests are inflated, nodes are oversized even when an autoscaler is running.

The problem with typical requests

Engineers set requests based on estimates or copied from a template:

resources:
  requests:
    cpu: "500m"
    memory: "512Mi"
  limits:
    cpu: "1000m"
    memory: "1Gi"

If the pod actually uses 80m CPU at p95 and 120Mi memory at p95, the request is 6× oversized for CPU and 4× oversized for memory. That node slot is claimed but 80% empty.

Using Goldilocks for recommendations

Goldilocks deploys VPA in recommendation mode per namespace and provides a dashboard showing actual usage vs current requests with recommended values:

helm repo add fairwinds-stable https://charts.fairwinds.com/stable
helm install goldilocks fairwinds-stable/goldilocks --namespace goldilocks
kubectl label namespace production goldilocks.fairwinds.com/enabled=true

Review recommendations weekly. Apply changes to staging first, monitor for a week, then apply to production.

Target resource settings

Value Setting
CPU request p95 observed usage + 20% headroom
CPU limit 2–4× CPU request (allow bursting)
Memory request p95 observed usage + 30% headroom
Memory limit 1.2–1.5× memory request (OOMKill > swap)

Karpenter: Intelligent Node Provisioning

The Cluster Autoscaler works by scaling pre-configured node groups up and down. It provisions nodes of one type and waits for the group to scale up. Karpenter takes a different approach: given pending pods, it evaluates their requirements and provisions exactly the most cost-effective node that satisfies them — in under 60 seconds.

Key Karpenter advantages

Best-fit instance selection. Instead of scaling a fixed node group, Karpenter evaluates all available instance types and picks the cheapest one that satisfies pending pod requirements. A pod needing 2 CPUs and 4GB RAM gets a node that fits that, not a general-purpose node 3× larger.

Automatic consolidation. Karpenter continuously evaluates whether running workloads can be packed onto fewer nodes. When consolidation is safe (respecting PDBs and do-not-disrupt annotations), it terminates underutilised nodes and reschedules their pods — without manual intervention.

Mixed capacity types in one pool. A single Karpenter NodePool can provision both on-demand and spot capacity, choosing spot opportunistically and falling back to on-demand when spot capacity is unavailable.

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: default
spec:
  template:
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot", "on-demand"]
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["c", "m", "r"]
      nodeClassRef:
        group: karpenter.k8s.aws
        kind: EC2NodeClass
        name: default
  disruption:
    consolidationPolicy: WhenUnderutilized
    consolidateAfter: 30s

Teams migrating from Cluster Autoscaler to Karpenter typically see 30–60% reduction in node costs from better bin-packing and spot adoption alone.


Spot Node Pools

Spot instances offer 60–80% discounts. On Kubernetes, they are most effective for:

Workload type Spot suitability
Stateless applications (>= 2 replicas) High — pods reschedule on interruption
CI/CD build runners High — jobs restart on new nodes
ML training with checkpointing High — resume from last checkpoint
Batch data processing High — idempotent retry
Stateful applications (databases) Low — interruption causes downtime
Single-replica critical services Low — no redundancy on interruption

Reliability through diversification

Spot interruptions are instance-type and zone specific. Requesting capacity across multiple instance families and availability zones dramatically reduces the probability of losing all spot capacity simultaneously:

requirements:
  - key: karpenter.sh/capacity-type
    operator: In
    values: ["spot"]
  - key: karpenter.k8s.aws/instance-category
    operator: In
    values: ["c", "m", "r"]   # multiple families
  - key: topology.kubernetes.io/zone
    operator: In
    values: ["us-east-1a", "us-east-1b", "us-east-1c"]  # multiple zones

Pod Disruption Budgets

Set PDBs on any workload that runs on spot nodes to ensure enough replicas stay healthy during interruption:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: api-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: api

Namespace Governance

Without namespace-level controls, a single misbehaving deployment can consume all cluster capacity:

ResourceQuota

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-quota
  namespace: team-alpha
spec:
  hard:
    requests.cpu: "20"
    requests.memory: 40Gi
    limits.cpu: "40"
    limits.memory: 80Gi
    pods: "50"

LimitRange (defaults for pods without explicit settings)

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: team-alpha
spec:
  limits:
  - default:
      cpu: "500m"
      memory: "256Mi"
    defaultRequest:
      cpu: "100m"
      memory: "128Mi"
    type: Container

Non-Production Environment Scheduling

Development and staging clusters often run 24/7 when they only need to run during business hours. A cluster running from 8am to 8pm weekdays operates for 60 hours per week instead of 168 — a 64% reduction in running time.

KEDA's cron scaler can scale deployments to zero replicas on a schedule. For the cluster itself, a Lambda/Cloud Function can call the Kubernetes API or node group scale-in API on a schedule.


Savings Summary

Optimisation Typical savings
Pod right-sizing 15–25% of compute cost
Karpenter vs Cluster Autoscaler 20–40% of node cost
Spot node pools (eligible workloads) 50–70% on those nodes
Non-production scheduling (off-hours) 50–70% of non-prod compute
Namespace quotas + waste elimination 5–15% miscellaneous

Applied together on a cluster without prior optimisation, total savings of 40–60% are consistently achievable. The key is sequencing: visibility first, pod right-sizing second, node optimisation third, governance fourth.


For help implementing Kubernetes cost optimisation as part of a broader cloud architecture engagement, see our Cloud Engineering capabilities or get in touch.

For context on the broader cloud cost picture beyond Kubernetes, our Cloud Cost Optimization guide covers the complete FinOps approach across AWS, Azure, and GCP.

Need Expert Guidance?

Planning custom software for your business?

Book a free consultation with our team to discuss architecture, product strategy, and the right build approach for your goals.

Book Free Consultation