Kubernetes Cost Optimization: Which Numbers Move the EKS Bill?

The short answer
  • The scheduler places pods by what they requested, not by what they use. Kubernetes says the request is what "the kube-scheduler uses to decide which node to place the Pod on", so your node bill is the sum of your deployments' asks.
  • An EKS cluster goes from $0.10 to $0.60 per cluster hour on the day its Kubernetes version leaves standard support, and the EXTENDED upgrade policy is the default on new and existing clusters. Nobody has to approve that change for it to appear on the invoice.
  • kube-proxy spreads traffic across pods "regardless of their node or AZ placement", so crossing an Availability Zone is the default rather than a mistake. The two cheapest fixes are annotations, not projects.
  • Lowering requests saves nothing by itself, because you keep the same nodes and they are emptier. Something has to reclaim them, which is the job Karpenter consolidation does.

Most Kubernetes cost optimization exercises open by looking at the cluster. Node count, instance families, a dashboard showing CPU averaging fourteen percent. All true, and almost none of it is where the money went.

Three things decide an EKS bill, and a node-level view shows none of them: requests somebody set two years ago and nobody revisited, a control plane that slid into extended support and went up six times, and pod traffic crossing Availability Zones because no Service was told not to.

So the position. Do not resize a single instance until you have read your requests and your cluster's support status. One of those is free to check, and the other is costing $365 a month per cluster while you do not.

Why does Kubernetes cost optimization stall on the control plane?

Because the control plane is the only line item with the cluster's name on it. EKS pricing puts standard support at "$0.10 per cluster per hour", roughly $73 a month, and a team that finds that number concludes either that EKS is cheap or that the savings must be in cheaper instances. Neither follows. The control plane is the one piece AWS actually operates; everything still yours on EKS is the rest of the bill, and most of it traces back to a decision in a YAML file.

Are you paying for what your pods use or what they asked for?

What they asked for. This is the most expensive misunderstanding in a Kubernetes account and it is stated plainly in the Kubernetes documentation on managing container resources: "When you specify the resource request for containers in a Pod, the kube-scheduler uses this information to decide which node to place the Pod on."

Requests. Not usage. The same page confirms the other half: "If the node where a Pod is running has enough of a resource available, it's possible (and allowed) for a container to use more resource than its request for that resource specifies." The scheduler packs nodes by summed requests, your node group scales to fit them, and you are billed for the nodes. A deployment that asks for 2 CPU and uses 200m has bought 1.8 CPU of EC2 that nothing will ever run on.

Third-party Helm charts carry a quieter version of the trap: "If you specify a limit for a resource, but do not specify any request ... then Kubernetes copies the limit you specified and uses it as the requested value." A generous limit with no request reserves the limit.

Order matters. Trimming requests alone changes nothing visible, because you keep the same machines, now emptier. Something has to reclaim them, which is what Karpenter's consolidation is for: it finds nodes that "can be removed as their workloads will run on other nodes in the cluster" or "replaced with lower priced variants".

One detail repays reading before you switch it on, because the timer is not what people assume. Karpenter "resets this timer whenever a pod is added to or removed from the node", so on a cluster of frequent short jobs a generous consolidateAfter means consolidation never fires at all. We watched that go unnoticed for a quarter.

Is your cluster quietly in extended support?

Possibly, and nobody had to do anything for it to happen. AWS gives a minor version "standard support in Amazon EKS for the first 14 months after it's released", then "it enters extended support for the next 12 months", per the EKS version lifecycle. The rate moves from $0.10 to "$0.60 per cluster per hour": same cluster, six times the price, $438 a month instead of $73.

The default does the work. "By default, for all new and existing clusters, the upgrade policy is set to EXTENDED", and the switch flips on a date rather than an event: "Billing for extended support starts at the beginning of the day that the version reaches end of standard support, in the UTC+0 timezone."

Hold today's calendar against that. Standard support for 1.33 ended on 29 July 2026, so a cluster still on 1.33 has billed at $0.60 an hour for three months. For 1.34 the date is 2 December 2026, five weeks away. And 1.31 leaves extended support entirely on 26 November 2026, after which AWS upgrades the control plane for you: "Automatic updates can happen at any time after the end of extended support date. You won't receive any notification before the update."

Read the next line of that FAQ carefully, because it causes an outage rather than an invoice. The automatic upgrade "includes only the Kubernetes control plane", and "Self-managed nodes and EKS Managed Node Groups will remain on the previous version". Your API server moves, your kubelets stay, and whether that is survivable depends on how far apart they already were.

One correction to something widely repeated: extended support can be turned off, it is just not a saving. "If you disable extended support, your cluster will be auto upgraded at the end of standard support." That is a scheduling decision rather than a billing one, and it belongs with the question of whether you could restore the cluster if an upgrade went badly.

Which traffic is crossing an Availability Zone for no reason?

More than you would guess, and by default rather than by mistake. AWS's own EKS networking cost guidance says kube-proxy distributes traffic "across all pods in the cluster, regardless of their node or AZ placement", which "can lead to cross-AZ traffic routing" and "inter-AZ data transfer charges in large deployments".

Four fixes, cheapest first, all from that page.

  • Load balancer target mode. In instance mode the load balancer hits a NodePort and kube-proxy forwards, possibly across a zone. In ip mode "network traffic is proxied from the load balancer directly to the destination Pod. As a result, there are no data transfer charges involved in this approach." AWS recommends ip mode outright. It is an annotation.
  • Same-zone Service routing. trafficDistribution: PreferClose went generally available in Kubernetes 1.33 and tells kube-proxy to prefer same-zone endpoints, falling back cluster-wide when there are none. In 1.35 it is renamed PreferSameZone, with PreferClose kept as an alias. AWS notes the older topology aware routing "is still in beta" while Traffic Distribution "prioritizes predictability instead".
  • Gateway endpoints for S3 and DynamoDB. "There are no hourly or data transfer costs associated with Gateway VPC Endpoints." If that traffic leaves through a NAT gateway today you pay hourly plus per-gigabyte processing for something free. AWS also suggests a NAT gateway per zone, which needs arithmetic first: you trade inter-AZ transfer for gateway hours.

One to leave alone: internalTrafficPolicy: Local looks like the stronger version of this, but "when there are no available node-local endpoints the traffic is dropped".

What can you run on Monday?

Three jobs, under an hour, and none changes anything.

First, find out which support tier you are paying for:

aws eks describe-cluster --name <cluster> \
  --query '{version: cluster.version, policy: cluster.upgradePolicy.supportType}'

aws eks describe-cluster-versions \
  --query 'clusterVersions[].{v: clusterVersion, status: status,
                              stdEnds: endOfStandardSupportDate}' \
  --output table

Match your version against that table. A status of EXTENDED_SUPPORT means $0.60 an hour today. A stdEnds date inside the next quarter means an upgrade to schedule rather than a surprise to absorb.

Second, measure the gap between requested and used. Needs metrics-server:

kubectl top nodes

kubectl describe node <node> | sed -n '/Allocated resources/,/Events/p'

The first prints what your nodes use; the second, what has been requested of them as a percentage of allocatable. Requests at 85 percent against usage at 12 percent is the ordinary finding, and the whole problem in two numbers: you bought the 85.

Third, read one annotation per load balancer: alb.ingress.kubernetes.io/target-type on your Ingresses, and service.beta.kubernetes.io/aws-load-balancer-nlb-target-type on NLB Services. Anything that is not ip is a change you can propose this week.

When is the expensive answer the right one?

Often enough that it needs saying before somebody takes a hatchet to production.

Do not collapse to a single Availability Zone. The egress saving is real, and it buys a cluster that dies with one zone. For a regulated lender it is not on the table anyway: the RBI's 2026 directions for commercial banks require at paragraph 62 that "The DC and DR sites shall be geographically well separated so that both the sites are not affected by a similar threat", and at paragraph 165 that "Periodicity of DR drills for critical information systems shall be at least on a half-yearly basis". The parallel obligations for an NBFC are in our read of the 2026 cybersecurity directions.

Some generous requests are correct. A JVM with a slow warm-up, a settlement path with a p99 somebody is measured on, a batch job whose peak looks nothing like its mean. Those want a request that matches the peak, not the average and an incident in March. Same judgement as working out which cost recommendations to ignore, where a tool calls a warm standby idle because it is.

And extended support is occasionally worth buying on purpose: $365 a month beats a rushed upgrade that breaks an admission webhook over a quarter end. Buy it with an end date written down, which is the difference between a decision and a default.

The short version

Run describe-cluster-versions this week and put your version's end-of-standard-support date next to your upgrade plan. If it has passed, you are paying $438 a month for the control plane and the remedy is an upgrade, not a negotiation.

Read requested CPU against used CPU on every node. Trim the asks that are obviously wrong, then let a consolidator reclaim the machines, because the first without the second saves nothing.

Move every load balancer to ip target mode, put trafficDistribution: PreferClose on your chattiest internal Services, and add gateway endpoints for S3 and DynamoDB. A morning's work against a line item nobody owns.

Then stop. Multi-AZ and headroom on the latency-critical path are costs worth defending, and an exercise that cannot name what it refused to cut has not finished.

This is engineering guidance, not legal advice. Regulations and their annexures are amended over time; read the current text and take your compliance position from counsel.

Why teams pick ReGoBs

We are a DevSecOps company in Kochi, working with fintech, crypto and AI teams across India, the UAE, the US and the UK. Infrastructure we build and run has secured over $3B in transaction volume at 99.9% uptime, for clients including BILLDESK and Coinshift and for teams backed by Y Combinator and Sequoia.

The work is built to survive an audit rather than to describe one: SOC 2 Type II, ISO 27001, PCI DSS and SLSA 3. Our VAPT team holds OSWE certification, has spoken at Defcon, BlackHat and OWASP, and has found critical CVEs in open source software.

We rank ourselves first in our guide to choosing a DevOps company for a fintech — and we say so on that page, in the second paragraph, rather than presenting it as a neutral list. Read the criteria there and hold us to them.

Want someone to read the cluster first?

Our free cloud audit covers the EKS accounts: which version each cluster runs and what support tier that puts it in, requested capacity against used, and where traffic leaves a zone on its way to a pod. You keep the findings whether or not you work with us.

Book the free audit What is still yours on EKS