09 · Cost Management & Billing¶
Every module in this course has said "remember to clean up" for a reason: GCP bills for what exists, not what you're actively using. This module is about the tools that stop a forgotten resource — or a genuine traffic spike — from becoming a surprise invoice: budgets, alerts, billing export, and labels that make a bill actually readable.
The billing account hierarchy¶
A billing account is linked to one or more projects; every resource's cost rolls up through its project to whichever billing account that project is linked to. Checking the link is the first troubleshooting step when "but I set a budget" turns out not to have applied to the resource in question:
gcloud billing projects describe gcp-mastery-path-123
# billingAccountName: billingAccounts/012345-6789AB-CDEF01
# billingEnabled: true
gcloud billing accounts list
# ACCOUNT_ID NAME OPEN
# 012345-6789AB-CDEF01 My Billing Account True
Budgets and alert thresholds¶
A budget doesn't cap spend or stop anything automatically by default — it sends notifications when spend crosses thresholds you define:
gcloud billing budgets create \
--billing-account=012345-6789AB-CDEF01 \
--display-name="Monthly Learning Budget" \
--budget-amount=50USD \
--threshold-rule=percent=0.5 \
--threshold-rule=percent=0.9 \
--threshold-rule=percent=1.0
gcloud billing budgets list --billing-account=012345-6789AB-CDEF01 \
--format="table(displayName,amount.specifiedAmount.units)"
# DISPLAY_NAME UNITS
# Monthly Learning Budget 50
At 50%, 90%, and 100% of $50 for the billing period, everyone with the Billing Account Administrator/Viewer role gets an email — nothing is throttled or shut off unless you separately wire a Pub/Sub notification to automation that reacts to it (see below).
Reacting to a budget alert automatically¶
Budgets can publish to a Pub/Sub topic on every threshold crossing, which a Cloud Function can consume to take real action (e.g. disabling billing on a runaway sandbox project — a drastic, last-resort move for real production projects, but reasonable for a personal learning project):
gcloud pubsub topics create budget-alerts
gcloud billing budgets create \
--billing-account=012345-6789AB-CDEF01 \
--display-name="Auto-capped Sandbox Budget" \
--budget-amount=20USD \
--threshold-rule=percent=1.0 \
--all-updates-rule-pubsub-topic=projects/gcp-mastery-path-123/topics/budget-alerts
A Cloud Function subscribed to budget-alerts receives a message with
costAmount, budgetAmount, and the project — from there it's ordinary
Pub/Sub-triggered code (Module 04) that calls gcloud billing projects
link --billing-account="" to disconnect billing (which also stops the
resources) if you choose to go that far.
Cost breakdown by label¶
Untagged resources produce a bill that's just one big number. Labels (key-value pairs on most GCP resources) let the billing export/reports break spend down by team, environment, or feature:
gcloud compute instances create web-vm-1 \
--zone=us-central1-a \
--machine-type=e2-medium \
--image-family=debian-12 \
--image-project=debian-cloud \
--labels=env=staging,team=platform
gcloud compute instances list --format="table(name,labels.list())"
# NAME LABELS
# web-vm-1 env=staging,team=platform
# Filter cost/inventory queries by label
gcloud compute instances list --filter="labels.env=staging"
Billing export to BigQuery¶
For real analysis (spend trends, per-service breakdowns, anomaly detection), export detailed billing data to BigQuery rather than reading the console UI by hand:
bq mk --dataset gcp-mastery-path-123:billing_export
gcloud billing accounts get-iam-policy 012345-6789AB-CDEF01
Billing export itself is configured in the Cloud Console under Billing →
Billing export (there's no gcloud command to enable it — the destination
dataset must exist first, which is the bq mk step above). Once flowing:
-- Total spend by service, last 30 days
SELECT
service.description AS service,
SUM(cost) AS total_cost
FROM `gcp-mastery-path-123.billing_export.gcp_billing_export_v1_*`
WHERE _PARTITIONTIME > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
GROUP BY service
ORDER BY total_cost DESC;
+------------------------+-------------+
| service | total_cost |
+------------------------+-------------+
| Compute Engine | 34.21 |
| Kubernetes Engine | 12.05 |
| Cloud Storage | 1.14 |
+------------------------+-------------+
Recommender: right-sizing and idle resources¶
GCP's Recommender service surfaces machine-generated cost suggestions based on actual usage, without you having to hunt manually:
gcloud recommender recommendations list \
--project=gcp-mastery-path-123 \
--location=us-central1-a \
--recommender=google.compute.instance.MachineTypeRecommender
Common recommenders worth checking periodically: idle VMs
(google.compute.instance.IdleResourceRecommender), oversized persistent
disks, and unattached IP addresses still being billed.
Committed use discounts (qualitative)¶
For steady, predictable long-running workloads, GCP offers Committed Use Discounts (CUDs) — a discount (often 20-57% depending on term/resource) in exchange for committing to 1 or 3 years of spend on a resource type/region. This is a purely financial commitment decision (not something to provision casually while learning) — mentioned here so the term is recognizable when it shows up in a real production cost review, not as something to try in a sandbox project.
Gotchas¶
- A budget alert is a notification, not a spending cap. Nothing stops automatically at 100% unless you build the Pub/Sub → Cloud Function → billing-disconnect pipeline yourself.
- Billing export only contains data from the point it was enabled forward — there's no retroactive backfill of cost history from before you turned it on.
- Labels must be added before they're useful for cost breakdown — labeling a resource after the fact only affects cost attribution going forward, not past usage.
- Recommender suggestions are based on historical usage patterns, so a newly created resource won't have recommendations yet — give it time before treating an empty recommendation list as "already optimal."
- Disabling billing on a project stops everything, hard — every resource in it stops functioning immediately; treat the auto-disconnect pattern above as a sandbox-only safety net, never a production pattern.
Cleanup¶
gcloud billing budgets delete <budget-id> --billing-account=012345-6789AB-CDEF01
gcloud compute instances delete web-vm-1 --zone=us-central1-a --quiet
bq rm -r -f gcp-mastery-path-123:billing_export
gcloud pubsub topics delete budget-alerts
Cheat sheet¶
| Command | Purpose |
|---|---|
gcloud billing projects describe |
Confirm which billing account a project is linked to. |
gcloud billing budgets create --threshold-rule=percent= |
Create a budget with alert thresholds. |
--all-updates-rule-pubsub-topic= |
Wire budget alerts to Pub/Sub for automation. |
--labels=key=value (on most create commands) |
Tag resources for cost attribution. |
bq mk --dataset |
Create the BigQuery dataset billing export writes into. |
gcloud recommender recommendations list |
Get machine-generated cost/right-sizing suggestions. |
How It Actually Works¶
GCP's billing pipeline aggregates usage, not cost, in near-real time — every resource emits usage records (vCPU-seconds, GB-months, API calls) that are rated against SKU pricing and rolled up into a cost export roughly every few hours, which is why the console's "current spend" figure is always an estimate lagging true usage rather than a live meter. Budgets and alerts work off this same rated-usage stream: a budget alert threshold doesn't stop spending by itself — it triggers a notification (and optionally a Pub/Sub message you can wire to a Cloud Function that actually disables billing or shuts down resources), which is why "set a budget" alone never prevents an overspend, only informs of one. Committed Use Discounts and Sustained Use Discounts work on opposite mechanisms: committed use is a contractual pre-commitment applied automatically as a rate discount on qualifying usage regardless of how much you actually run, while sustained use discounts are calculated retroactively based on the fraction of the billing month a given resource class actually ran, with no upfront commitment required.
Exercise¶
Create a $10 budget with 50%/90%/100% thresholds on your billing account,
and confirm the alert email arrives after crossing a threshold (or simulate
it by lowering the amount below current spend). Label two or three existing
resources with env= and team=, then run a list --filter="labels.env=..."
query to confirm the filter works. If billing export is enabled, run the
BigQuery query above and identify your single most expensive service this
month.