I Cut GKE Costs Without Rebuilding the Platform

·5 min read

Listen to this article· 3 min

Photo by Cosmin Andrei Buzamat on Unsplash

When I joined the team, one of the first things I noticed was not a broken application.

It was the opposite.

Everything was working.

The applications were running, the dashboards were available, the pipelines were doing their job...

And yet, something didn't feel right.

The GKE cluster was heavily over-provisioned.

We had more compute capacity than our workloads actually needed, which meant we were paying for resources that were sitting there doing very little.

So the question became:

How can we use only the resources we actually need without rebuilding the whole platform?

That turned into one of my first real cloud cost-optimization projects.

The problem was not the applications

The existing setup was based on relatively large, fixed-size nodes.

The idea was simple:

WorkloadsLarge node poolAlways available capacity

It worked.

But Kubernetes doesn't charge you based on how much CPU your applications feel like using.

You're paying for the infrastructure you provision.

And when you have large nodes sitting mostly idle, that idle capacity becomes an expensive decoration.

When I looked at the actual workloads, the picture was different from what the infrastructure suggested.

The workloads did not consistently need huge machines.

So we had a mismatch:

  • What workloads neededmodest
  • What infrastructure providedlarge

That gap was where the cost was hiding.

The obvious solution wasn't the one I wanted

One option would have been to redesign a large part of the cluster.

New node pools.

Different machine types.

Manual workload placement.

More operational rules.

More things to maintain.

But I didn't want the solution to become a new problem.

The goal was not:

"Let's redesign Kubernetes."

The goal was:

"Let's make GKE provision infrastructure closer to what our workloads actually require."

That's when I started looking seriously at Node Auto-Provisioning, or NAP.

The idea behind NAP

The concept is actually pretty simple.

Instead of deciding in advance:

"We need 3 machines of this exact size."

we give Kubernetes enough information about what the workloads need and let GKE create suitable node pools when necessary.

GKE's node pool auto-creation extends the cluster autoscaler and can create node pools based on the resource requests and hardware requirements of pending Pods. It can also scale those pools down and remove empty auto-created pools when they're no longer needed.

So instead of:

Fixed infrastructure
  • Large nodelots of free capacity
  • Large nodelots of free capacity

the idea becomes:

Demand-based infrastructure
  1. 01Workload"I need this much"
  2. 02
    GKE
    Smaller nodeAnother node

The infrastructure becomes more flexible.

Resource requests suddenly became important

This is where I learned something that sounds basic but becomes very important in the real world:

Kubernetes resource requests are not just configuration details.

They influence scheduling.

They can also influence how GKE provisions infrastructure.

GKE uses CPU, memory, ephemeral storage, and other workload requirements when deciding what auto-created nodes can satisfy a pending Pod.

That means this:

resources:
  requests:
    cpu: "500m"
    memory: "512Mi"

is telling Kubernetes something important:

"This workload needs roughly this amount of guaranteed capacity for scheduling."

So the more accurate those requests are, the better the infrastructure can match the workload.

And this gave me another lesson:

Cloud cost optimization can start inside the Kubernetes manifest.

NAP was not magic

There is an important detail here.

Enabling NAP doesn't magically make badly defined workloads efficient.

If your Pods request far more CPU and memory than they actually need, GKE will work from those requests.

And GKE's documentation explicitly warns that resource requests need to be large enough for the workload to function correctly.

So the process was more like:

The actual process
  1. 01Inspect workloads
  2. 02Understand resource usage
  3. 03Review requests / limits
  4. 04Enable better autoscaling
  5. 05Let GKE choose suitable capacity
  6. 06Observe
  7. 07Tune

Not:

Turn on NAP💰 magically cheaper

Sadly, cloud does not work like that.

I checked.

From fixed capacity to demand-based capacity

The biggest change was moving away from the assumption that the cluster needed to maintain a large amount of capacity all the time.

GKE's cluster autoscaler already helps resize node pools based on workload demand, while node pool auto-creation goes a step further by allowing GKE to create node pools that fit workloads that cannot currently be scheduled.

That creates a much more dynamic model:

Low demand
Fewer nodes
Lower cost
High demand
More capacity
More nodes when needed

For a workload platform where usage changes over time, this is much more interesting than simply buying bigger machines "just in case."

The numbers mattered

After optimizing the setup, the cost reduction became significant.

The first phase of the work reduced the GKE cost by roughly 50% compared with the previous setup.

That was the moment when cost optimization stopped being a theoretical exercise.

We had a very simple proof:

GKE cost
  • Before💰 100%
  • After💰 ~50%

Same platform.

Same workloads.

A different way of provisioning the infrastructure.

And there was still more room to optimize.

The bigger lesson wasn't NAP

I could write this article as:

"Here's how to enable Node Auto-Provisioning."

But that misses the interesting part.

The important lesson for me was changing the question.

Before:

How big should our cluster be?

After:

How much infrastructure does the workload actually need right now?

That's a very different way of thinking.

It also pushed me toward a broader cloud engineering mindset.

When you work mostly on application code, infrastructure can feel like something underneath the application.

When you start working with cloud platforms, infrastructure becomes part of the application design.

CPU matters.

Memory matters.

Node types matter.

Scaling matters.

Idle resources matter.

And eventually:

money matters.

Cost is an engineering metric

One of the biggest things I learned from this work is that cost should not be treated as a finance-team problem.

It is also an engineering signal.

If a workload needs:

The workload needs
  • 2 CPU
  • 4 GB RAM

but your architecture permanently reserves:

The architecture reserves
  • 16 CPU
  • 64 GB RAM

that is not only a billing problem.

It can be an architecture problem.

The infrastructure is telling you something about your design.

And sometimes the answer isn't "buy more."

Sometimes it's:

Use less.

What I would do again

Today, when I see a Kubernetes cluster with large fixed nodes, I don't immediately ask:

"Which cheaper machine type should we use?"

I first ask:

  • What are the workloads actually requesting?
  • What are they actually using?
  • How much of the cluster is idle?
  • Can autoscaling handle the variability?
  • Can GKE provision capacity dynamically?
  • Are the workloads correctly configured for that model?

Only then do I look at the infrastructure itself.

Because sometimes the biggest optimization isn't changing the machine.

It's changing how you decide when you need the machine.

Final thought

When I joined the team, I didn't expect one of my early cloud tasks to be about reducing Kubernetes costs.

I certainly didn't expect the solution to be less about adding infrastructure and more about letting the platform provision only what the workloads needed.

That experience changed how I look at GKE.

A cluster doesn't need to be large because your application might need it.

It should be able to respond when your workloads actually need capacity.

And that's probably one of the most useful things I've learned while moving deeper into cloud engineering:

The best infrastructure isn't always the infrastructure you provision. Sometimes it's the infrastructure you allow the platform to provision for you.

…

Up to 50 claps per reader

All posts
Share