DEV Community

iapilgrim
iapilgrim

Posted on

GCP The Hard Way — Part 7: Finding and Eliminating Cloud Waste

Introduction

Cloud cost inefficiency rarely comes from a single dramatic error —
it accumulates from small, individually reasonable-looking decisions:
an oversized VM chosen "to be safe," a static IP reserved for a test
that was never cleaned up, a storage bucket without a lifecycle
policy. This post deliberately introduces four common sources of cloud
waste, lets them run long enough to appear in billing data, and walks
through identifying and remediating each using Billing Reports and
Recommender.

Solution overview

┌─────────────────────────────────────────────┐
│                 Sources of waste               │
├─────────────────────────────────────────────┤
│ 1. Oversized Compute Engine instance           │
│ 2. Cloud Storage without lifecycle policy      │
│ 3. Reserved static IP with no attached resource │
│ 4. Autoscaling ceiling set without usage data   │
└─────────────────────────────────────────────┘
                    │
                    ▼
        ┌───────────────────────┐
        │   Billing Reports +     │
        │   Recommender API       │
        └───────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Prerequisites

  • A Budget Alert configured on the billing account (recommended threshold: $25) before beginning
  • At least 3–4 days of elapsed time between provisioning and analysis, since billing trend data needs a meaningful window to be useful

Walkthrough

Step 1: Provision the waste sources

Oversized instance for a lightweight workload:

gcloud compute instances create oversized-vm \
  --zone=asia-southeast1-a --machine-type=n2-standard-8 \
  --image-family=debian-12 --image-project=debian-cloud
Enter fullscreen mode Exit fullscreen mode

Storage without a lifecycle policy:

gcloud storage buckets create gs://<PROJECT_ID>-waste-bucket \
  --location=asia-southeast1
Enter fullscreen mode Exit fullscreen mode

Idle static IP:

gcloud compute addresses create unused-static-ip --region=asia-southeast1
Enter fullscreen mode Exit fullscreen mode

Autoscaling ceiling set without usage justification:

gcloud compute instance-groups managed set-autoscaling waste-mig \
  --zone=asia-southeast1-a --max-num-replicas=20 --min-num-replicas=2 \
  --target-cpu-utilization=0.8
Enter fullscreen mode Exit fullscreen mode

Allow this configuration to run for 3–4 days before proceeding to
analysis.

Step 2: Review Billing Reports

In Billing → Reports, filter by the time range covering the
provisioning period and group by Service, then by SKU for finer
granularity. Rank the four sources by actual cost — results frequently
diverge from intuition; for example, an idle static IP typically costs
far less than an oversized VM, but this should be confirmed against
your own data rather than assumed.

Step 3: Query Recommender for automated findings

gcloud recommender recommendations list \
  --project=<PROJECT_ID> --location=asia-southeast1-a \
  --recommender=google.compute.instance.MachineTypeRecommender

gcloud recommender recommendations list \
  --project=<PROJECT_ID> --location=global \
  --recommender=google.compute.address.IdleResourceRecommender
Enter fullscreen mode Exit fullscreen mode

Coverage gap to note: Recommender does not currently surface
missing storage lifecycle policies as a recommendation — this class of
waste requires manual review of bucket configuration and billing
trends, reinforcing that automated tooling should supplement, not
replace, periodic manual cost review.

Step 4: Remediate

gcloud compute instances stop oversized-vm --zone=asia-southeast1-a
gcloud compute instances set-machine-type oversized-vm \
  --zone=asia-southeast1-a --machine-type=e2-small
gcloud compute instances start oversized-vm --zone=asia-southeast1-a
Enter fullscreen mode Exit fullscreen mode
cat > lifecycle.json <<'EOF'
{
  "rule": [
    {"action": {"type": "SetStorageClass", "storageClass": "COLDLINE"},
     "condition": {"age": 30}},
    {"action": {"type": "Delete"}, "condition": {"age": 365}}
  ]
}
EOF
gcloud storage buckets update gs://<PROJECT_ID>-waste-bucket \
  --lifecycle-file=lifecycle.json
Enter fullscreen mode Exit fullscreen mode
gcloud compute addresses delete unused-static-ip --region=asia-southeast1 --quiet

gcloud compute instance-groups managed set-autoscaling waste-mig \
  --zone=asia-southeast1-a --max-num-replicas=4 --min-num-replicas=1 \
  --target-cpu-utilization=0.7
Enter fullscreen mode Exit fullscreen mode

The revised autoscaling ceiling of 4 should be justified by observed
peak usage during the test window, not set arbitrarily — an
unjustifiably high ceiling creates cost exposure risk even without
generating cost directly, since a traffic spike or application bug
could scale the fleet far beyond actual need.

Measuring savings

Allow 1–2 additional days after remediation, then compare Billing
Reports for the same resource categories before and after the change:

Category Cost before (monthly, extrapolated) Cost after % reduction
Compute (VM resize)
Storage (lifecycle policy)
Idle static IP + LB
Autoscaling ceiling

Clean up resources

gcloud compute instances delete oversized-vm --zone=asia-southeast1-a --quiet
gcloud storage rm -r gs://<PROJECT_ID>-waste-bucket
Enter fullscreen mode Exit fullscreen mode

Conclusion

Automated recommenders are a useful first pass, but this exercise
shows they have blind spots — notably storage lifecycle policies — that
still require deliberate, periodic manual review. Building this review
into a regular cadence, rather than a one-time cleanup, is the
practical difference between temporary savings and sustained cost
efficiency.

In Part 8, we close the series by reversing roles: designing a
broken system for someone else — or a future version of yourself — to
diagnose.

Top comments (0)