How To

GCP Cost Optimization: 7 Places Your Google Cloud Bill Is Leaking

GCP cost optimization guide: 7 places your Google Cloud bill leaks — orphaned disks, CUD gaps, idle VMs — with real gcloud commands to find each one.

·
gcpcostoptimizationfinops
Cover Image for GCP Cost Optimization: 7 Places Your Google Cloud Bill Is Leaking

GCP Cost Optimization: 7 Places Your Google Cloud Bill Is Leaking

Google Cloud has a strange property among the big three: the platform already knows where you're wasting money. Active Assist generates idle-VM findings, machine-type recommendations, and commitment purchase suggestions for every project you run — and in most organizations, nobody reads them. The findings sit in a console page no one visits, quietly refreshing while the bill compounds.

So GCP cost optimization is rarely a detection problem in the pure sense. It's a looking problem. On bills between $10K and $500K/month, the waste concentrates in the same seven places, and for most of them Google will hand you the evidence if you ask. This guide asks. For each leak: what it is, why it forms, one real gcloud command or exact console path to find it, and the range it typically costs. Budget about an hour to run everything.

If you run AWS alongside GCP, the AWS version of this list follows the same logic with different tooling.

1. Orphaned persistent disks and snapshots

The leak. Deleting a Compute Engine VM does not delete its persistent disks unless auto-delete was enabled — and for anything beyond the boot disk, it almost never is. The disk detaches, keeps its full provisioned billing rate (roughly $0.04/GB-month for standard PD, around $0.17 for SSD), and reads by nothing, forever. Snapshots accumulate in parallel: migration backups, pre-upgrade safety copies, the output of a scheduled snapshot policy whose cleanup half was never written.

How it forms. Individual disks look cheap and deletion feels irreversible, so the cleanup ticket never wins a sprint. Meanwhile a 500 GB SSD orphan bills about $85 a month for holding data nothing reads.

Find it. Unattached disks are the ones with no users:

gcloud compute disks list \
  --filter="-users:*" \
  --format="table(name,zone,sizeGb,type,lastAttachTimestamp)"

For snapshots, sort by age and compare against your stated retention policy (if you don't have one, that's the first finding):

gcloud compute snapshots list \
  --sort-by=creationTimestamp \
  --format="table(name,diskSizeGb,storageBytes,creationTimestamp)"

Google also surfaces these through the idle resource recommenders. Snapshot a disk before deleting it if you're nervous — the snapshot costs a fraction of the provisioned disk.

What it's worth. Usually 1–3% of the bill. The smallest number on this list, but the fastest to collect: nothing depends on an unattached disk by definition.

2. Idle or oversized instances — the GCP rightsizing gap

The leak. VMs whose CPU has averaged single digits for a month. Some are abandoned experiments that should be deleted outright. Most are honest workloads on dishonest machine types — an n2-standard-16 doing an n2-standard-4 job because someone padded the estimate at launch and no one has touched it since.

How it forms. Sizing happens once, under uncertainty, with a safety margin. Revisiting it means a restart, a change window, and an owner — three things idle VMs conveniently lack.

Find it. This is where GCP rightsizing gets easier than on other clouds: the machine type recommender has already analyzed 8 days of usage for every VM and drafted the resize. Pull its findings per zone:

gcloud recommender recommendations list \
  --project=PROJECT_ID \
  --recommender=google.compute.instance.MachineTypeRecommender \
  --location=us-central1-a

Swap in google.compute.instance.IdleResourceRecommender to catch VMs Google thinks should simply stop existing. Sanity-check memory-bound workloads before applying — the recommender sees CPU well and installed-agent memory metrics only where the Ops Agent runs.

What it's worth. Typically 10–20% of Compute Engine spend. Each one-step machine-type reduction roughly halves that instance's cost.

3. Committed use discounts — coverage gaps and the SUD ceiling

The leak. Every hour of stable baseline you run at on-demand rates is a discount you declined. Teams often believe sustained use discounts already handle this. They don't. SUDs apply automatically to eligible machine types the longer they run in a month — but they cap out (up to 30% on N1, up to 20% on N2 and C2 for a full month) and never touch E2 or Tau instances at all. Committed use discounts go deeper: roughly 37% off for a one-year resource commitment and up to 55% for three years on most machine families, in exchange for committing to a baseline you were going to run anyway.

How it forms. Two failure modes. Teams that never buy CUDs because commitment feels like a bet nobody owns. And teams that bought them once, let them expire, and silently reverted to list price — a lapsed commitment changes nothing about your infrastructure except the unit rate, so no alert fires.

Find it. List commitments and check end dates:

gcloud compute commitments list \
  --format="table(name,region,plan,status,endTimestamp)"

Then open Billing → Cost optimization → Committed use discount analysis in the console, which charts coverage of eligible spend over time. Healthy coverage of a stable baseline sits around 60–80% — below that you're paying a voluntary premium; above it you risk paying for commitment you can't use when load shifts.

What it's worth. Frequently the single biggest lever here: 10–25% of total compute spend for accounts that never systematized commitments.

4. Unused static IPs and idle load balancers

The leak. A reserved static external IP attached to nothing bills roughly $7–8 a month for the privilege. A load balancer's forwarding rules bill hourly (around $18/month for the first five) whether or not a single request arrives. Both get created per experiment, per migration, per "temporary" endpoint — and outlive all three.

How it forms. IPs get reserved "so the address doesn't change" for services that were deleted a year ago. Load balancers survive because nobody is sure what still points at them, and nobody wants to find out in production.

Find it. Reserved-but-unattached addresses:

gcloud compute addresses list \
  --filter="status=RESERVED" \
  --format="table(name,region,address,addressType)"

For load balancers, list the forwarding rules with gcloud compute forwarding-rules list, then check each one's request count in Monitoring → Metrics Explorer (the loadbalancing.googleapis.com/https/request_count metric for HTTP(S) load balancers). Two weeks of near-zero requests means the load balancer is decorative. Releasing an unused IP is instant and carries no risk; pricing details are in the VPC network pricing docs.

What it's worth. Small in absolute terms — usually $50–500/month — but zero-risk to reclaim, and a reliable proxy for how much other hygiene debt the project carries.

5. Over-provisioned Cloud SQL instances

The leak. A db-custom-8-32768 averaging 6% CPU with a dozen connections, sized for launch-day traffic that never came. Databases attract the worst over-provisioning on any cloud because nobody resizes a production instance that's working — and with high availability enabled, every wasted vCPU bills twice.

How it forms. The person who sized it has left. Resizing means a brief failover. "It's the database" ends the conversation.

Find it. Open Monitoring → Metrics Explorer and chart cloudsql.googleapis.com/database/cpu/utilization over 30 days for each production instance — then chart database/memory/utilization too, because CPU alone flatters memory-bound databases. Sustained CPU under 20% with memory under 60% is a strong down-size candidate. Google also ships a dedicated over-provisioned Cloud SQL recommender that runs this analysis for you:

gcloud recommender recommendations list \
  --project=PROJECT_ID \
  --recommender=google.cloudsql.instance.OverprovisionedRecommender \
  --location=us-central1

What it's worth. Cloud SQL is often 15–30% of a SaaS bill, and teams that audit it typically find 20–40% savings within the database line itself.

6. Network egress and inter-zone traffic

The leak. Data transfer is the line item nobody designed for. Inter-zone traffic bills about $0.01/GB in each direction — trivial per gigabyte, four figures per month once chatty microservices sit in different zones. Internet egress starts around $0.085–0.12/GB depending on destination, and Cloud NAT adds a per-GB processing charge on top (network pricing).

How it forms. Nothing in a Terraform plan says "this service mesh will move 30 TB a month between zones." Regional GKE clusters spread pods across three zones by design, which is excellent for availability and quietly expensive for chatty east-west traffic.

Find it. In Billing → Reports, set Group by → SKU and search the results for "egress", "data transfer", and "inter-zone". The pattern to hunt is a steadily growing inter-zone SKU under Networking — that's architecture, not usage, and it won't fix itself. The classic remediations: co-locate chatty service pairs in one zone where the availability math allows it, enable Private Google Access so traffic to Google APIs skips Cloud NAT, and put a CDN in front of anything serving large objects to the internet.

What it's worth. Highly variable — near zero for monoliths, 5–15% of the bill for distributed architectures.

7. Non-production environments running 24/7

The leak. Dev, staging, QA, and demo projects billing 168 hours a week to support a work week that needs roughly 60 of them. Everything running outside those hours — VMs, GKE nodes, Cloud SQL — is close to 65% pure waste for that environment.

How it forms. Shutdown has no owner, and one broken Monday-morning startup buys the initiative a year of "we tried that."

Find it. In Billing → Reports, group by Project and compare each non-prod project against what a business-hours schedule would cost. Then fix it with instance schedules, which handle both the stop and the politically crucial morning start:

gcloud compute resource-policies create instance-schedule nonprod-hours \
  --region=us-central1 \
  --vm-start-schedule="0 8 * * MON-FRI" \
  --vm-stop-schedule="0 20 * * MON-FRI" \
  --timezone="America/Chicago"

Attach the policy to non-prod VMs and the Monday startup problem disappears. Cloud SQL supports on-demand stop and start; GKE non-prod node pools can scale to zero off-hours.

What it's worth. Non-prod commonly runs 20–40% of total spend at mid-market companies; scheduling cuts that portion by half or more.

Why monthly GCP cost optimization reviews fail

Add the ranges up and a realistic mid-market deployment carries 20–35% addressable waste. The natural response is a monthly review meeting. Here's why that consistently underdelivers on Google Cloud specifically.

First, Recommender findings are perishable. Recommendations refresh continuously as usage changes — a rightsizing suggestion pulled during your review reflects the last 8 days, and by the time the resize ticket reaches a sprint three weeks later, the recommendation may have changed or the state marked stale. Findings reviewed monthly are acted on at a cadence the data wasn't designed for.

Second, projects proliferate. GCP makes project creation cheap and every team uses it — per-environment projects, per-experiment projects, sandbox projects. Every gcloud command above is scoped to one project, so a 40-project organization needs 40 passes, or scripting, or an honest admission that only the top five projects ever get reviewed. Waste migrates to the projects nobody audits.

Third, the math of latency. A monthly review means the average finding is 15 days old before anyone sees it. On a $100K/month bill with 20% addressable waste, each month of detection delay costs roughly $20K — and the review is a day of engineer time, so it gets skipped in release-heavy months, which are precisely the months that generate the most waste.

The way to reduce a Google Cloud bill durably isn't reviewing harder. It's making detection continuous — starting with making the manual pass systematic.

Make GCP cost optimization continuous

Part two of this series — a DIY GCP cost audit using only native tools — turns the checks above into a repeatable audit built on Recommender, Active Assist, and the BigQuery billing export. When you'd rather not schedule audits at all, CloudThinker's CostOps agent connects to your GCP projects through read-only access and runs all seven checks continuously, across every project, surfacing findings the day they appear — with any remediation gated behind your approval. Start free — 100 premium credits, no card required.