Cost Optimization
Topic 56

Cost Optimization

FinOpsCost

Cloud cost is not a technical problem; it is an operational discipline. The same workload can cost a factor of ten more or less depending on which services it runs on, how those services are configured, and how their consumption is monitored. The eleven previous chapters introduced fifty-one services; almost every one of them has a pricing dimension that can dominate the bill if ignored. This topic is about the practices that keep cost under control as an ongoing engineering activity, not as an end-of-quarter panic.

The single most important principle: cost engineering is continuous, not episodic. Teams that "do a cost optimization sprint" once a year save 30% temporarily, then watch the bill creep back up over the next twelve months. Teams that wire cost visibility into normal operations — the bill is on the dashboard, the budget alerts are wired to the on-call rotation, the per-environment tags are required — save 30% permanently and never need the panic sprint.

Pricing Models

GCP services fall into a small set of pricing shapes. Knowing which shape applies to which service is half the battle. Compute services usually price by time and size: Compute Engine charges per second for the machine type; Cloud Run charges per request and per vCPU-second of execution time when the container is active. Managed databases price by storage plus compute: Cloud SQL bills the instance hour and the storage GB; Spanner bills processing units and storage; BigQuery bills storage at one rate, queries either by scanned bytes (on-demand) or by slot-hour (capacity-based).

Cloud Storage has three dimensions: storage by GB-month at a rate that varies by class (Standard, Nearline, Coldline, Archive), operations by count (Class A for writes/lists, Class B for reads), and egress when bytes leave the bucket. Pub/Sub bills by bytes published and delivered. Cloud Load Balancing charges for forwarding rules and processed bytes. The bill is the sum of these dimensions across every resource you own.

Three pricing levers cut spend dramatically without changing the architecture: committed use discounts (CUDs) for steady-state compute, where a 1- or 3-year commitment saves 25–55% on Compute Engine, Cloud Run, GKE, Cloud SQL, and Spanner; sustained use discounts applied automatically to Compute Engine instances that run a large fraction of the month; and Spot VMs for fault-tolerant workloads, which cost 60–91% less than on-demand at the price of preemption. The right mix usually has CUDs covering the baseline and on-demand or Spot absorbing the peaks.

Right-Sizing and Auto-Scaling

A right-sized resource is one whose capacity matches the actual demand, not the demand someone imagined at provisioning time. The default failure mode is over-provisioning — picking a four-vCPU VM because it might be needed, watching it run at 8% utilization, paying for the other 92% forever. The Active Assist recommender in Cloud Console surfaces oversized Compute Engine instances and idle resources continuously; the fix is usually a single command.

Auto-scaling does right-sizing automatically for workloads that fit it. Cloud Run scales from zero to thousands of containers in seconds; you pay only for the requests served. GKE Autopilot scales nodes against pod resource requests; idle namespaces cost nothing because no nodes are running for them. The discipline is to set the right resource requests, not just limits — requests drive scheduling and node count, limits only prevent runaway containers.

Schedule non-production environments. A staging environment that runs at full capacity overnight and over the weekend costs the same as production while delivering zero value during those hours. Cloud Scheduler triggering Cloud Functions that scale down GKE node pools or shut down Compute Engine instances during off hours typically cuts non-prod spend by 60–70%.

Storage Tiering

Cloud Storage's four classes have dramatically different costs per GB-month: Standard is the baseline; Nearline is roughly half; Coldline is roughly a quarter; Archive is roughly an eighth. The cost of retrieval climbs in the opposite direction — pulling from Archive costs more than pulling from Standard, and Archive has a minimum 365-day storage duration with early-deletion fees. The right placement is the one where the cumulative storage savings outweigh the retrieval cost over the data's expected lifetime.

Object Lifecycle Management automates the tiering. A rule like "transition objects older than 30 days to Nearline, older than 90 days to Coldline, older than 365 days to Archive, delete after 7 years" runs continuously without any per-object code. The same mechanism deletes old object versions in a versioned bucket, which prevents the silent accumulation of cost from soft-deleted data.

For block storage, the equivalent discipline is to delete snapshots and unattached Persistent Disks. Orphan disks from deleted VMs are one of the most common sources of slow-growing waste; a weekly Cloud Asset Inventory query surfaces them. Filestore has minimum capacity floors per tier — the Basic tier starts at 1 TB; allocating 2 TB "to be safe" doubles a fixed cost that does not auto-scale down.

Egress and Network Costs

Network egress is the most overlooked cost dimension. Intra-zone traffic is free; cross-zone traffic in the same region is cheap; cross-region within GCP costs more; egress to the public internet costs the most; egress to specific destinations through certain peering arrangements can cost differently again. A workload that hits a cross-region pattern not by design but by accident — a Cloud Run service in europe-west1 calling Spanner in us-central1 — can pay 10x more than the intra-region equivalent.

Co-locate services that talk to each other. The Cloud Run service, its database, its Cloud Storage bucket, and its Pub/Sub topic should normally all live in the same region. Multi-region or dual-region deployments are appropriate for global services but should be a deliberate choice, not a default. Cloud CDN in front of static assets cuts egress dramatically by serving cached responses from the edge; the cache hit rate on a well-tuned CDN is the single biggest egress lever for content-heavy sites.

Private connectivity to managed services — Private Google Access, Private Service Connect — keeps traffic inside Google's network and avoids any internet egress. This is a security best practice and a cost best practice at the same time.

BigQuery Cost Patterns

BigQuery deserves its own treatment because its pricing dimensions are unusual. On-demand pricing bills by bytes scanned per query; a careless SELECT * FROM big_table can cost tens of dollars in seconds. Capacity-based pricing reserves slots for predictable spend; the right choice depends on workload variability — capacity is cheaper for steady analytics, on-demand is cheaper for sporadic queries.

The practices that cut BigQuery spend without rewriting queries: partition large tables by date so a WHERE date = '...' filter scans only the relevant partitions; cluster on the columns that filter most queries so BigQuery prunes blocks within a partition; materialize repeatedly computed aggregates as scheduled materialized views; cap query bytes with the --maximum-bytes-billed flag or per-project query quotas to prevent runaway scans.

Avoid SELECT * in production code; name the columns. BigQuery's columnar storage means each named column is a separate cost, and unnecessary columns multiply the bill. A team that switches a daily reporting query from SELECT * to SELECT col1, col2, col3 often cuts cost by 70% or more in one commit.

FinOps Practices

FinOps is the discipline of running cloud cost as an engineering practice with shared accountability across engineering, finance, and product. Three practices form the spine.

First, label everything. Every resource gets labels for cost center, team, environment, and service. Without labels, the bill is one giant unstructured number; with labels, the bill is a sliceable dataset that answers questions like "which team consumed 80% of the BigQuery spend last month?". Enforce labels with organization policies so untagged resources cannot be created in production projects.

Second, export the billing data to BigQuery and build a small analytics layer over it. The Cloud Console billing reports are useful for at-a-glance views; serious analysis happens in BigQuery against the exported data. Data Studio dashboards on top of the export give product and finance teams self-serve visibility without engineer intervention.

Third, budget alerts and anomaly detection. Set budgets at the project or label level; configure Cloud Billing budget alerts to fire at 50%, 90%, 100% of spend; wire the alerts to the same channels as production alerts so engineering sees them. Recommender's anomaly detection surfaces sudden spend changes — a misbehaving job that scanned a terabyte instead of a gigabyte shows up the day it happens, not at the end of the month.

Continuous Cost Engineering

The teams that keep cost under control treat it as an ongoing measurement, similar to how SRE treats reliability. A monthly cost review covers: top spending services, biggest month-over-month changes, recommender findings (idle resources, oversized instances, CUD opportunities), egress trend, BigQuery scan trend, unattached disks, and the labels coverage report. The output is action items with owners and due dates, tracked like any other engineering work.

Pair cost engineering with reliability engineering — the error budget and the cost budget answer the same question for different axes. "Can we afford a more reliable architecture?" and "can we afford a less efficient implementation?" are the same conversation, and the right answer comes from the data, not from anyone's intuition.

Cost Anti-Patterns

Recurring shapes of wasted spend on GCP:

Cost Anti-Patterns
  • The forgotten resource — a Compute Engine instance, a Cloud SQL database, a load balancer, a Spanner instance that nobody is using. Inventory and label discipline find these; without labels they hide forever.
  • Non-prod running 24/7 — staging, dev, demo environments at full size, all night, every weekend. Schedule them off; the savings are usually 60–70% of non-prod spend with no engineering impact.
  • SELECT * in BigQuery production code — every reporting job scanning every column of every row. Named columns and partition filters typically cut the bill by an order of magnitude.
  • Cross-region egress by accident — service in one region calling its database in another because nobody noticed during deploy. Region pinning at the project policy level and explicit checks at provisioning time prevent this.
  • Over-provisioning for safety — picking the next bigger VM "to be safe", reserving twice the BigQuery slots in case of spikes, allocating 2 TB of Filestore when 1 TB would do. The cost of over-provisioning is real; the cost of starting smaller and scaling is usually trivial.
  • No CUDs on steady-state compute — leaving a database that has been running for two years at on-demand prices. 1-year and 3-year commitments save 25–55% with effectively no operational change.
  • Snapshot and disk debris — old Persistent Disk snapshots kept "just in case", unattached disks from deleted VMs, orphaned IPs reserved but not attached. All quietly bill. Lifecycle automation cleans them.
  • Annual panic sprint instead of continuous review — one big optimization push per year, then a year of regrowth. The bill is not a project; it is a metric. Treat it like one.
Best Practices
  • Label every resource with cost center, team, environment, and service. Enforce labels via organization policies so untagged resources cannot exist in production.
  • Export billing data to BigQuery; build Data Studio dashboards on top. The Console billing report is for glances; serious analysis happens against the export.
  • Right-size with Active Assist recommendations. Treat the recommender like a backlog — review weekly, act on the easy wins, evaluate the harder ones.
  • Use committed use discounts on steady-state compute and databases. 1-year CUDs are usually safe; 3-year for the most stable workloads.
  • Schedule non-prod environments off during nights and weekends. The savings are immediate and the operational impact is none.
  • Partition and cluster large BigQuery tables. Cap query bytes per project. Never use SELECT * in production queries.
  • Set Cloud Billing budget alerts at the project and label level; wire them to engineering channels so spend anomalies are visible the day they happen, not at the end of the month.
Comparable services AWS Cost Optimization pillar · Cost Explorer + BudgetsAzure Microsoft Cost Management + Advisor

Knowledge Check

A team has been running the same Cloud SQL instance and a steady fleet of 20 Compute Engine VMs for over a year. Which pricing lever cuts the most spend with the least operational change?

  • Migrate the whole fleet and the database to Spot VMs immediately to capture the 60–91% preemptible discount
  • Apply 1- or 3-year committed use discounts on the baseline — 25–55% savings on Compute Engine and Cloud SQL with effectively no operational change
  • Migrate the Cloud SQL instance to a Cloud Run hosted database so the steady fleet captures the cheaper serverless per-request pricing instead of paying for an always-on instance
  • Switch the whole workload to a multi-region setup so the added redundancy drives the per-unit cost down

A staging environment runs a full GKE cluster, Cloud SQL instance, and Cloud Run services 24/7. The team uses staging only during business hours. What is the highest-ROI cost action?

  • Migrate the entire staging stack to a smaller, cheaper GCP region with lower per-unit compute and storage pricing
  • Replace the staging Cloud SQL instance with Firestore to take advantage of its pay-per-operation serverless pricing
  • Use Cloud Scheduler to scale GKE node pools to zero, stop the Cloud SQL instance, and let Cloud Run scale to zero outside business hours — usually 60–70% savings on non-prod spend with no engineering impact
  • Rewrite all of the staging Cloud Run services onto Cloud Functions to save on per-invocation memory billing, since functions only allocate memory while a request is actively executing and bill nothing in between

A team's BigQuery bill has tripled in two months. Investigation shows a daily reporting job uses SELECT * FROM events against a 50 TB table without partition filters. What is the immediate fix?

  • Move the table to capacity-based slot pricing so the daily full-table scan cost stops mattering on the bill
  • Name the actual columns the report needs and add a partition filter — typically cuts the cost by an order of magnitude in one commit; longer-term, partition by date and cluster on common filter columns
  • Delete and recreate the 50 TB events table from scratch to reset the accumulated query bill
  • Export the 50 TB table to Cloud Storage and point the daily reporting job at the exported CSV files instead, so the daily scan reads from cheap object storage rather than from billed BigQuery bytes-scanned pricing

A media team stores 100 TB of historical video files that are rarely accessed but must be kept for seven years for compliance. What is the right storage configuration?

  • Keep all 100 TB of video in the Standard class so any file is available for fast retrieval at any time
  • Move all 100 TB straight to the Archive class on day one to minimize the monthly storage cost
  • Object Lifecycle Management rule: transition objects to Nearline after 30 days, Coldline after 90 days, Archive after 365 days, delete after seven years — the cumulative storage savings dwarf the rare retrieval cost
  • Store the 100 TB of video as Persistent Disk snapshots and rely on incremental snapshot pricing, which works out cheaper per terabyte than Cloud Storage for rarely accessed cold data held over the full seven-year window

A team deploys a new Cloud Run service in europe-west1 that calls Spanner in us-central1 on every request. The bill rises sharply. What is the cause?

  • Spanner simply costs more per node in us-central1 than the same instance would in europe-west1
  • Cross-region egress on every request — intra-region traffic is essentially free, cross-region within GCP costs far more. Co-locate Cloud Run and Spanner in the same region by default; multi-region is a deliberate choice, not an accident
  • Cloud Run cold starts carry an extra per-instance surcharge whenever the service runs outside the us-central1 region
  • Spanner cannot serve traffic across regions at all, so each europe-west1 request silently forces it to spin up and pay for a duplicate set of redundant compute nodes in us-central1 to satisfy the call, which is what drives the sharp bill increase

A FinOps lead wants to know which team consumed 80% of the BigQuery spend last month. The labels coverage is below 30%. What is the first action?

  • Migrate the analytics workload off BigQuery onto a different service to simplify the overall cost model
  • Enforce labels via org policy so new resources require team, environment, cost_center, service labels; backfill labels on existing resources; export billing data to BigQuery; build a Data Studio dashboard sliced by label
  • Ask each team to estimate their share of the BigQuery spend from recollection of what they ran, collect those self-reported percentages, and divide last month's total bill across the teams in proportion to the estimates they hand back
  • Disable BigQuery access for any team that cannot immediately justify its usage to the FinOps lead

Why is "do a cost optimization sprint once a year" a worse strategy than continuous cost engineering?

  • Annual sprints are impossible to schedule reliably because GCP changes its published service pricing almost every single week
  • Costs creep back up between sprints — the team saves 30% during the sprint, then the bill climbs over the next twelve months as new unlabeled, oversized, and forgotten resources accumulate. Continuous review with budget alerts, recommender follow-through, and label enforcement keeps the savings permanent
  • Annual optimization sprints end up requiring substantially more dedicated headcount than continuous cost engineering, because each yearly sprint pulls a large cross-functional team off feature work for weeks to reconstruct what changed, whereas continuous review spreads the same total effort thinly across far more people every single day of the year
  • Continuous cost engineering is a separate Google managed service that requires its own paid license to enable

You got correct