Start with visibility, not cuts

Before changing anything, get a per-namespace, per-workload breakdown of spend. Guessing which service is expensive wastes more engineering time than the savings you'll find.

Tools worth setting up first:

  • Kubecost or your cloud provider's cost allocation tags
  • Resource requests/limits reporting via kubectl top and the metrics-server
Signal Tool What it tells you
Per-namespace spend Kubecost Where the money actually goes
Requested vs. used metrics-server How much is over-provisioned
Idle nodes Cluster autoscaler logs Whether you can scale down

The checklist, in priority order

  1. Right-size requests, not just limits. Over-requested CPU/memory reserves capacity nothing uses. Compare requests against actual kubectl top pod usage over a two-week window.
  2. Turn on the Cluster Autoscaler (or Karpenter). Static node pools sized for peak load are the single biggest source of waste.
  3. Use spot/preemptible nodes for stateless workloads. Anything that tolerates restarts — batch jobs, stateless APIs behind a load balancer — belongs on spot capacity.
  4. Set Pod Disruption Budgets before you touch autoscaling. Otherwise spot reclaims or scale-downs turn into incidents.
  5. Consolidate small workloads onto fewer, larger nodes. Bin-packing efficiency drops fast with lots of tiny nodes; fewer larger nodes reduce per-node overhead.
  6. Audit persistent volumes. Orphaned PVCs from deleted StatefulSets are a quiet, recurring cost.
  7. Downscale non-production environments outside working hours. A simple CronJob that scales dev/staging deployments to zero overnight pays for itself in a week.
  8. Revisit egress traffic patterns. Cross-AZ and cross-region traffic between services adds up; co-locate chatty services where you can.

Example: scheduled scale-down CronJob

apiVersion: batch/v1
kind: CronJob
metadata:
  name: scale-down-staging
spec:
  schedule: "0 20 * * 1-5"
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: scaler
          containers:
            - name: scaler
              image: bitnami/kubectl
              command:
                - /bin/sh
                - -c
                - kubectl scale deployment --all --replicas=0 -n staging
          restartPolicy: OnFailure

The result

Applying steps 1–4 alone typically recovers 25–40% of compute spend without touching application code. Steps 5–8 are smaller wins individually, but they compound — and unlike a one-time right-sizing pass, they keep paying off as the cluster grows.