Cost Management and Billing
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. See the section bibliography for the reference material consulted while preparing these pages. |
Commands and console steps below were checked against the gcloud CLI reference and the Cloud Billing documentation on
2026-10-01; re-verify against the linked pages if you are reading this later. The gcloud billing command group,
gcloud compute commitments, gcloud recommender and the bq tool are generally available, while spend-cap
budgets are a Preview feature at the time of writing. Billing accounts use the placeholder ID 000000-000000-000000,
projects use my-project-id and <project-number>, and e-mail addresses are on example.com. Prices change often, so
this page never quotes a rate: every cost statement links to a dated, official source instead.
Every other page in this section describes a service; this one explains how the money side of Google Cloud works and what the Bookshelf stack costs to run. It covers the billing-account and project model and who may do what with it, budgets and alerts (and why they are not a cap), the tools that estimate cost before deploying, the ways to pay less for steady usage, labels for chargeback, the billing export to BigQuery, the FinOps tooling that finds waste, the free programs, per-second billing and a qualitative table of the hidden cost drivers. The first budget alert and the free-trial details are introduced in Getting Started with Google Cloud; the project layout is in Resource Hierarchy and Projects. For the Azure and AWS counterparts see Cost Management and Cost Management and Support.
Billing accounts and projects
Google Cloud separates who pays from who owns resources:
- Cloud Billing account
-
A resource that holds the payment profile (a card or bank account for self-serve accounts, or invoice terms for invoiced accounts) and receives the usage charges of every project linked to it. It has an ID of the form
000000-000000-000000and exists independently of the resource hierarchy, although an organization can own it. - Project
-
The container of resources, APIs, IAM bindings and quotas. A project is linked to at most one billing account at a time, and a project with no billing account cannot use paid services (its resources are shut down when the link is removed).
- Payments profile
-
The Google-wide record of the legal entity, address and payment instruments. One payments profile can back several billing accounts. It is administered in the Google payments center, not in the Cloud console.
One billing account can pay for any number of projects, in different folders or even different organizations, and every charge on the invoice is attributed to the project that incurred it. This is why projects are the natural unit for cost reporting (see Resource Hierarchy and Projects). Billing accounts come in two kinds: self-serve accounts, paid online by card or direct debit, charged automatically, and the only kind that can start the free trial; and invoiced accounts, paid by invoice or wire after an agreement with Google or a reseller, typically with a credit limit and monthly invoicing. Resellers and large customers can additionally create sub-accounts beneath a master billing account to separate customers or departments; each sub-account has its own projects, budgets and reports, and the parent sees the roll-up.
legal entity and payment method"] --> BA["Billing account
000000-000000-000000"] BA --> P1["Project: bookshelf-dev"] BA --> P2["Project: bookshelf-stg"] BA --> P3["Project: bookshelf-prod"] BA --> BUD["Budgets and alerts"] BA --> EXP["Billing export
to BigQuery"] BA --> CUD["Commitments
(shared by linked projects)"] P3 --> USE["Usage: Cloud Run, Cloud SQL,
Cloud Storage, load balancer"] USE -. "charges flow to" .-> BA
IAM roles on billing
Access to billing is granted with IAM roles on the billing account (or on the organization, for creation rights), separately from the roles on projects. A project owner can therefore deploy resources without being able to see or change how they are paid for.
| Role | What it allows |
|---|---|
Billing Account Creator ( |
Create new self-serve billing accounts. Granted on the organization. |
Billing Account Administrator ( |
Full control of one billing account: link and unlink projects, manage budgets, set IAM on the account, view costs. |
Billing Account User ( |
Link projects to the billing account. Does not allow viewing costs or changing the account. |
Billing Account Costs Manager ( |
Manage budgets and cost-management settings such as exports and commitments, and view costs, without linking projects or editing IAM. |
Billing Account Viewer ( |
View cost information, transactions and budgets, read-only. |
Project Billing Manager ( |
On a project: link or unlink that project to a billing account the person is allowed to use. Combine with Billing Account User on the account. |
Linking a project needs two permissions together: permission to associate resources with the billing account
(roles/billing.user on the account) and permission to change the project’s billing assignment (the Project
Owner or Project Billing Manager role on the project). Following least privilege, give finance people
roles/billing.viewer or roles/billing.costsManager through a Google group, give platform teams
roles/billing.user, and keep roles/billing.admin to two or three break-glass administrators. The full role
descriptions are in the official access-control page.
In the Google Cloud console
-
Open the navigation menu, then Billing. If you manage several billing accounts, choose Go to linked billing account or the account selector at the top.
-
Open Billing account management (or Account management) to see the account ID, the linked payments profile, the status and the list of Projects linked to this billing account.
-
To link or move a project, choose Actions next to the project, then Change billing, pick the target billing account and confirm.
-
To review who has access, open Account management, select the account and use the Permissions panel to grant a role such as Billing Account Viewer to a group.
With the gcloud CLI
# Listing and describing billing accounts needs at least the Billing Account Viewer role
gcloud billing accounts list --filter="open=true" \
--format="table(name,displayName,open,masterBillingAccount)"
gcloud billing accounts describe 000000-000000-000000
# Which billing account pays for this project?
PROJECT_ID=$(gcloud config get-value project)
gcloud billing projects describe "$PROJECT_ID"
# Link, move and unlink a project
gcloud billing projects link "$PROJECT_ID" --billing-account=000000-000000-000000
gcloud billing projects unlink "$PROJECT_ID"
# Projects paid by one account
gcloud billing projects list --billing-account=000000-000000-000000
# Give the finance group read-only access to costs, and the platform group the right to link projects
gcloud billing accounts add-iam-policy-binding 000000-000000-000000 \
--member="group:finance@example.com" --role="roles/billing.viewer"
gcloud billing accounts add-iam-policy-binding 000000-000000-000000 \
--member="group:platform@example.com" --role="roles/billing.user"
gcloud billing accounts get-iam-policy 000000-000000-000000
Unlinking is destructive: the project’s paid resources stop and may be deleted after a grace period, so never script
unlink without the safeguards discussed in Reacting to a budget programmatically.
Per-second billing and what else is metered
Most of the compute and database products that bill by time meter it per second, which is why starting a large VM for a ten-minute test costs about ten minutes and not an hour. The details differ by product, so check the unit of each:
| Product | How usage is metered |
|---|---|
Compute Engine VMs and GKE nodes |
Per second, with a one-minute minimum per VM start. Stopped VMs stop paying for vCPU and memory but keep paying for their disks and reserved IP addresses. |
Cloud Run and Cloud Run functions |
vCPU-seconds and GiB-seconds, rounded up to 100 milliseconds, plus a per-request fee in request-based billing; instance-based billing meters the whole instance lifetime. Minimum instances bill at an idle rate. |
GKE Autopilot |
Per second for the vCPU, memory and ephemeral storage requested by running Pods, plus a cluster management fee. |
Cloud SQL, AlloyDB, Memorystore |
Per second or per hour of instance time for the provisioned shape, plus storage, backups and network, whether or not queries are running. |
Cloud Storage |
GiB-months of data stored by class (including soft-deleted bytes), operations per class, retrieval and early-deletion fees, and network egress. |
BigQuery |
Either bytes processed per query (on-demand) or slot-hours (capacity editions), plus storage billed in logical or physical bytes. |
Network |
GiB of traffic leaving a region, zone or Google Cloud, and hourly fees for forwarding rules, NAT gateways and IP addresses. |
Per-second metering rewards short-lived infrastructure (CI runners, batch jobs, preview environments) and punishes forgotten always-on components, which is the theme of the later Hidden cost drivers section. The exact minimums and rounding for each product are on its own pricing page; the individual service pages in this section state them in their Pricing paragraph.
Budgets and alerts
A budget tracks the spend of a billing account (or a scope inside it) against an amount and sends notifications
when thresholds are crossed. A budget alerts; it does not cap spending. Services keep running and charges keep
accruing after the last threshold, and the data behind the alerts lags real usage (usage and costs are reported with
a delay of up to a day or more for some services), so the final bill can exceed the budget. Treat budgets as an early
warning system and combine them with quotas, max-instances limits and a programmatic reaction when a hard limit is
required.
A budget is defined by:
- Scope
-
Which charges count: the whole billing account, selected projects, selected services, selected labels or a combination. Savings and credits can be included or excluded.
- Time range
-
A calendar month, quarter or year, or a custom date range.
- Amount
-
A specified amount in the billing currency, or the last period’s spend so the budget follows the previous month.
- Thresholds
-
Percentages of the amount (for example 50, 90 and 100), each on actual spend or forecasted spend. A forecast threshold fires when Google predicts you will exceed the amount before the period ends.
- Notifications
-
E-mail to billing administrators and users (on by default through IAM), Cloud Monitoring notification channels such as Slack or PagerDuty, and optionally a Pub/Sub topic for programmatic reactions.
Spend-cap budgets are a newer, Preview-stage capability that can stop supported spend when a limit is reached. They are not generally available and have service coverage restrictions, so read the budget documentation before depending on them, and do not treat Preview behaviour as a production safety net.
In the Google Cloud console
-
Open Billing, select the billing account, then Budgets & alerts, and choose Create budget.
-
Name it
bookshelf-monthly. Under Scope, choose the time range Monthly and select only the Bookshelf projects (and optionally a label filter such asapp:bookshelf). Choose Next. -
Under Amount, choose Specified amount and enter the target, for example 200 in your billing currency, or choose Last month’s spend.
-
Under Actions, set threshold rules for 50%, 90% and 100% of Actual spend and add 100% of Forecasted spend.
-
Under Manage notifications, keep Email alerts to billing admins and users, optionally add a Cloud Monitoring channel and Connect a Pub/Sub topic to this budget, then choose Finish.
With the gcloud CLI
gcloud services enable billingbudgets.googleapis.com cloudbilling.googleapis.com
PROJECT_ID=$(gcloud config get-value project)
PROJECT_NUMBER=$(gcloud projects describe "$PROJECT_ID" --format="value(projectNumber)")
gcloud billing budgets create \
--billing-account=000000-000000-000000 \
--display-name="bookshelf-monthly" \
--budget-amount=200USD \
--calendar-period=month \
--filter-projects="projects/$PROJECT_NUMBER" \
--threshold-rule=percent=0.5 \
--threshold-rule=percent=0.9 \
--threshold-rule=percent=1.0 \
--threshold-rule=percent=1.0,basis=forecasted-spend
gcloud billing budgets list --billing-account=000000-000000-000000 \
--format="table(name,displayName,amount.specifiedAmount.units)"
Run gcloud billing budgets create --help for the complete flag list, including the label and service filters, the
credit-treatment option and the Pub/Sub and notification-channel flags. A budget can also be declared in Terraform,
which fits the practice of keeping billing guardrails in version control:
resource "google_billing_budget" "bookshelf" {
billing_account = "000000-000000-000000"
display_name = "bookshelf-monthly"
budget_filter {
projects = ["projects/<project-number>"]
}
amount {
specified_amount {
currency_code = "USD"
units = "200"
}
}
threshold_rules { threshold_percent = 0.5 }
threshold_rules { threshold_percent = 0.9 }
threshold_rules { threshold_percent = 1.0 }
threshold_rules {
threshold_percent = 1.0
spend_basis = "FORECASTED_SPEND"
}
}
Reacting to a budget programmatically
Budgets can publish a JSON message to a Pub/Sub topic every time spend is evaluated, so automation can react: post
to a chat channel, scale a service to zero, or, as a last resort, disable billing on a project. Each message carries the
budget display name, the cost so far, the budget amount, the currency, the period start and, when a threshold has just
been crossed, alertThresholdExceeded. The wiring is a topic, a budget that publishes to it and a subscriber, here a
Cloud Run function (see Cloud Run Functions and Events):
gcloud pubsub topics create budget-alerts
# Update the budget to publish to the topic
BUDGET_ID=$(gcloud billing budgets list --billing-account=000000-000000-000000 \
--filter="displayName=bookshelf-monthly" --format="value(name.basename())")
gcloud billing budgets update "$BUDGET_ID" \
--billing-account=000000-000000-000000 \
--notifications-rule-pubsub-topic="projects/$PROJECT_ID/topics/budget-alerts"
The function receives the message through an Eventarc Pub/Sub trigger. A cautious handler compares costAmount with
budgetAmount, and only when spend is at or above the limit calls the Cloud Billing API to remove the billing link
of one named, non-production project. With the Java client library
(com.google.cloud:google-cloud-billing from the libraries-bom) and Application Default Credentials:
package com.example.bookshelf.cost;
import com.google.cloud.billing.v1.CloudBillingClient;
import com.google.cloud.billing.v1.ProjectBillingInfo;
import com.google.cloud.billing.v1.UpdateProjectBillingInfoRequest;
import java.io.IOException;
public class BillingKillSwitch {
private static final String PROJECT = "projects/bookshelf-sandbox-id";
public static void disableBilling() throws IOException {
try (CloudBillingClient client = CloudBillingClient.create()) {
// An empty billing account name unlinks the project from billing
ProjectBillingInfo info = ProjectBillingInfo.newBuilder().setBillingAccountName("").build();
client.updateProjectBillingInfo(UpdateProjectBillingInfoRequest.newBuilder()
.setName(PROJECT)
.setProjectBillingInfo(info)
.build());
}
}
}
Reference: Programmatic budget notifications, Disable billing usage with notifications, Cloud Billing client library for Java
The caveats make this a blunt instrument, which is why it is shown for a sandbox project and never for production:
-
Latency. The notification arrives after the cost data does, so spend that already happened (and some that is still in flight) is not undone; the bill can still pass the limit.
-
It stops everything. Unlinking billing shuts down paid resources in the project, which may delete data and cannot be undone cleanly. Some resources keep incurring charges until deleted, and re-enabling does not restore deleted data.
-
Permissions. The function’s service account needs
roles/billing.projectManageron the project androles/billing.useron the account, which is a powerful combination; give it to a dedicated service account used by nothing else. -
Scope. A budget covering the whole billing account would disable the wrong project; scope each kill-switch budget to one project.
For production, prefer gentler reactions: alert a human, lower max-instances on Cloud Run services, scale a node
pool down, and rely on quotas as a hard ceiling.
Estimating cost before you deploy
Estimating beats surprises. Three tools cover the planning phase:
- Pricing calculator
-
A web tool where you add products, choose regions and sizes and get a monthly estimate, with sections for Compute Engine, Cloud Run, Cloud Storage, Cloud SQL and most other services. Estimates can be saved and shared by link or exported to CSV, and the result reflects list prices, so apply your negotiated discount and commitments separately.
- Cloud Billing Catalog API
-
The machine-readable list of every service and SKU with its price tiers, useful to build cost checks into scripts and pull requests.
- Product pricing pages
-
Each service has a pricing page with examples, the free tier and the SKU units. They are the authoritative source for rounding and minimums.
In the Google Cloud console
-
Open the pricing calculator in a browser (it does not need an account) and choose Add to estimate.
-
Choose Cloud Run, set the region (for example
us-central1), the billing mode, CPU, memory, requests per month and average request time, then add Cloud SQL, Cloud Storage and Cloud Load Balancing for the Bookshelf shape. -
Review the Estimated monthly cost and the per-service breakdown, choose Share or Save estimate and keep the link with the architecture documentation.
-
In the console, the Billing > Reports and Cost table pages then show what actually accrued, to compare with the estimate.
With the gcloud CLI
There is no gcloud command for the calculator, but the Catalog API is a plain REST endpoint that accepts an access
token:
gcloud services enable cloudbilling.googleapis.com
TOKEN=$(gcloud auth print-access-token)
# Find the service ID of Cloud Run in the catalog
curl -s -H "Authorization: Bearer $TOKEN" \
"https://cloudbilling.googleapis.com/v1/services?pageSize=500" \
| jq -r '.services[] | select(.displayName=="Cloud Run") | .name'
# List the SKUs and unit prices of that service (replace SERVICE_ID with the value printed above)
curl -s -H "Authorization: Bearer $TOKEN" \
"https://cloudbilling.googleapis.com/v1/services/SERVICE_ID/skus?currencyCode=USD&pageSize=20" \
| jq '.skus[] | {description, category: .category.resourceFamily, regions: .serviceRegions}'
For bulk analysis, enable the pricing export to BigQuery described in Exporting billing data to BigQuery instead of paging through the API. The Terraform and gcloud workflows for building the infrastructure being estimated are in Infrastructure as Code.
Paying less for steady usage
Pricing is pay as you go with no minimum, and there are four ways to reduce the rate for predictable or flexible workloads.
Sustained use discounts
Sustained use discounts (SUDs) are automatic: certain VM families get a discount that grows as they run for a larger share of the month, with nothing to buy or configure. They apply to selected older general-purpose and compute- or memory-optimized machine series, not to the newest series such as E2, which already have a lower list price and no automatic discount. Because the applicable series change, check the official table before assuming a discount. Sustained use discounts do not combine with committed use discounts on the same resources, which take precedence.
Committed use discounts
Committed use discounts (CUDs) trade a one- or three-year commitment for a lower rate. There are two families:
- Resource-based commitments
-
You commit to an amount of vCPU, memory, GPUs or local SSD in a region (and, for Compute Engine, a machine family). The commitment is a Compute Engine resource that is applied automatically to matching usage in any project of the billing account unless sharing is restricted. You pay for the committed resources every hour whether or not you use them, so commit to the steady baseline, not the peak.
- Spend-based (flexible) commitments
-
You commit to an hourly dollar amount of eligible spend, and receive a discount on that usage as credits. Flexible commitments for compute cover several machine families and are not tied to one region, and spend-based commitments also exist for databases and analytics such as Cloud SQL, Memorystore, Spanner, AlloyDB and BigQuery capacity. Cloud Run and GKE Autopilot have commitment options of their own.
Commitments generally cannot be cancelled, so size them from the rightsizing recommendations and several weeks of steady usage. Coverage and the exact products, terms and discount percentages change between releases of the programme, so read the CUD overview and the per-product pages rather than relying on remembered numbers.
Spot VMs and Spot Pods
Spot VMs use spare Compute Engine capacity at a deep discount and can be reclaimed (preempted) with a short
warning, with no maximum runtime. They suit stateless, restartable work: CI runners, batch jobs, rendering and
the interruptible node pools of GKE (Spot node pools, or Spot Pods on Autopilot). They are a poor fit for the
orders-api database or anything that cannot tolerate eviction. Spot pricing is dynamic, so check the Spot pricing
page rather than hard-coding an assumption. See Compute Engine and
GKE Clusters.
Cheaper by design
Several of the largest savings come from architecture, not discounts: scale to zero with Cloud Run, choose Cloud Storage classes and lifecycle rules for cold data (see Cloud Storage), keep traffic inside a region, cache at the edge with Cloud CDN (see Cloud CDN and Media CDN) and turn off dev environments outside working hours.
In the Google Cloud console
-
Open Billing, select the billing account, then Commitments.
-
Choose Purchase commitment and pick the commitment type (for example Compute Engine flexible commitment or Compute Engine resource-based), the term (one or three years) and, for resource-based commitments, the region, machine family and amounts.
-
Review the price breakdown and the warning that the commitment cannot be cancelled, then confirm.
-
Use the Committed use discounts analysis or Commitments views and the commitment recommendations in the FinOps hub (see below) to check coverage and utilization.
With the gcloud CLI
gcloud services enable compute.googleapis.com recommender.googleapis.com
REGION=us-central1
# List existing commitments and inspect one
gcloud compute commitments list --format="table(name,region.basename(),plan,status,endTimestamp)"
gcloud compute commitments describe COMMITMENT_NAME --region="$REGION"
# Purchase a one-year resource-based commitment for 4 vCPU and 16 GB of E2 capacity
# (this is a binding purchase: run it only after checking the price and the recommendation)
gcloud compute commitments create bookshelf-baseline \
--region="$REGION" \
--plan=12-month \
--type=GENERAL_PURPOSE_E2 \
--resources=vcpu=4,memory=16GB
# Commitment recommendations produced by Active Assist
gcloud recommender recommendations list \
--project="$(gcloud config get-value project)" \
--location="$REGION" \
--recommender=google.compute.commitment.UsageCommitmentRecommender
Flexible (spend-based) commitments are purchased in the console or through the Cloud Commerce Consumer Procurement
API rather than with a dedicated gcloud command, so use the console for them.
Labels for cost allocation
Labels are user-defined key-value pairs attached to resources. They are the standard way to slice a bill by application, environment, team or cost centre, because labels set on a resource (and on a project) are copied into the billing export and can be used as budget and report filters. A resource may carry up to 64 labels. Keys and values are lowercase letters, digits, underscores and hyphens, keys start with a letter and both are limited to 63 characters. Not every service supports labels, and labels on a project are applied to the usage that project generates, while resource labels apply to that resource’s own SKUs.
Labels are different from tags (see Resource Hierarchy and Projects): tags are managed resources used for conditional IAM and organization policies, while labels are free-form metadata for reporting. For chargeback, agree on a small mandatory set, for example:
| Label key | Meaning and example value |
|---|---|
|
The application or product: |
|
The deployable unit: |
|
The life-cycle stage: |
|
The finance code charged back: |
|
The team that answers for the resource: |
Enforce the set with infrastructure as code and, in an organization, with an organization policy or a CI check on Terraform plans, since labels that are optional are quickly forgotten. Remember that labels apply from the moment they are added; they are not retroactive in billing data.
In the Google Cloud console
-
Open the resource in its console page, for example Cloud Run > catalog-api, and choose Edit and deploy new revision or the Labels tab.
-
Add the label pairs
app,component,envandowner, then save or deploy. -
For a project, open IAM & Admin > Settings (or Manage resources), choose the project and Labels, and add
app:bookshelfandenv:prod. -
In Billing > Reports, use Filters > Labels or Group by > Label to see the cost per label value.
With the gcloud CLI
PROJECT_ID=$(gcloud config get-value project)
REGION=us-central1
BUCKET=bookshelf-uploads-$PROJECT_ID
# Project-level labels
gcloud projects update "$PROJECT_ID" --update-labels=app=bookshelf,env=prod,cost_center=cc1042
# Cloud Run service
gcloud run services update catalog-api --region="$REGION" \
--update-labels=app=bookshelf,component=catalog-api,env=prod,owner=team-books
# Cloud Storage bucket
gcloud storage buckets update "gs://$BUCKET" \
--update-labels=app=bookshelf,component=uploads,env=prod
# Compute Engine VM, Cloud SQL instance
gcloud compute instances add-labels bookshelf-vm --zone="${REGION}-a" --labels=app=bookshelf,env=prod
gcloud sql instances patch bookshelf-catalog --update-labels=app=bookshelf,component=catalog-db,env=prod
# Find resources by label across services
gcloud asset search-all-resources --scope="projects/$PROJECT_ID" --query="labels.app=bookshelf" \
--format="table(assetType,name)"
Exporting billing data to BigQuery
The Cloud console reports are good for browsing, but chargeback, anomaly hunting and dashboards need the raw line items. Cloud Billing export writes them to a BigQuery dataset you own, where you query them with SQL and visualize them in Looker Studio. Several exports exist, each enabled separately per billing account:
- Standard usage cost
-
Daily-granularity cost data per SKU, project, location and label, in a table named
gcp_billing_export_v1_followed by the billing account ID with underscores. Enough for most chargeback. - Detailed usage cost
-
Everything in the standard export plus resource-level fields (such as the specific VM, bucket or Cloud Run revision), in
gcp_billing_export_resource_v1_followed by the account ID. Larger and more granular, so it costs more to store and query. - Pricing
-
The list prices of SKUs, in
cloud_pricing_export, for joining usage with price tiers. - Commitment (CUD) analysis
-
Data to evaluate commitment coverage, utilization and savings. Its availability and shape are newer, so check the export documentation.
Exports begin when you enable them and are not generally back-filled for earlier months, so enable them on day one,
before you need them. The dataset’s location is fixed at creation, so choose a multi-region such as US or EU
that matches your reporting needs and data-residency rules. Enabling an export needs Billing Account Administrator (or
Costs Manager) on the account and BigQuery permissions on the destination project.
In the Google Cloud console
-
Create the destination dataset first: open BigQuery, choose the project, Create dataset, name it
billing_export, pick the location (for exampleUS) and Create dataset. -
Open Billing, select the billing account, then Billing export.
-
On the Standard usage cost tab, choose Edit settings, select the project and the
billing_exportdataset and choose Save. Repeat on the Detailed usage cost and Pricing tabs as required. -
Wait a few hours for the first rows, then open BigQuery and check for the new tables.
With the gcloud CLI
Turning the export on is a console-only step. Everything around it, the dataset and the analysis, is scriptable with the
bq tool:
gcloud services enable bigquery.googleapis.com
PROJECT_ID=$(gcloud config get-value project)
# Destination dataset, in a multi-region (the location cannot be changed later)
bq --location=US mk --dataset \
--description="Cloud Billing export" \
"$PROJECT_ID:billing_export"
# Grant the finance group read access to the dataset
bq show --format=prettyjson "$PROJECT_ID:billing_export" > dataset.json
# (edit the access array to add {"role":"READER","groupByEmail":"finance@example.com"}, then)
bq update --source dataset.json "$PROJECT_ID:billing_export"
# Cost for the last 30 days by service and by the app label
cat > cost-by-service.sql <<'SQL'
SELECT
service.description AS service,
(SELECT value FROM UNNEST(labels) WHERE key = 'app') AS app,
ROUND(SUM(cost) + SUM(IFNULL((SELECT SUM(c.amount) FROM UNNEST(credits) c), 0)), 2) AS net_cost,
currency
FROM `my-project-id.billing_export.gcp_billing_export_v1_000000_000000_000000`
WHERE usage_start_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
GROUP BY service, app, currency
ORDER BY net_cost DESC
LIMIT 20;
SQL
bq query --use_legacy_sql=false < cost-by-service.sql
The query adds the (negative) credit amounts to the gross cost to give the net cost, and filters on
usage_start_time because the table is partitioned by it, which keeps the bytes scanned (and therefore the query
cost, see AI, Data and Analytics Overview) low. Labels are an array of key-value
records, hence the sub-select. Add a dry run with bq query --dry_run before running broad queries over the
detailed export.
Reports, anomalies and the FinOps hub
Before data reaches BigQuery, the console already offers a lot:
- Reports
-
Billing > Reports charts cost over time with filters (projects, services, SKUs, labels, locations) and grouping, with options to include credits and discounts. It is the quickest answer to what changed this week.
- Cost table and cost breakdown
-
Tabular views of cost, discounts and credits down to the SKU, and a waterfall from usage cost to the invoice total.
- Cost anomaly detection
-
Automatic detection of spend that deviates from the historic pattern, shown in the console and available as alerts, at no extra charge.
- FinOps hub
-
A single console page that gathers optimization opportunities: idle resources, rightsizing, commitment recommendations, an estimate of monthly savings and the utilization of existing commitments.
- Active Assist recommenders
-
The machine-generated recommendations behind the hub, available through the Recommender API: idle VMs, disks and IP addresses, VM rightsizing, unattended projects and commitment sizing.
- Cost and Carbon footprint
-
Reports that allocate emissions per project alongside the cost, useful for sustainability reporting.
In the Google Cloud console
-
Open Billing > Reports, set the time range, choose Group by > Service and Filters > Projects for
bookshelf-prod, and look for unexpected jumps. -
Open Billing > Cost table for a downloadable breakdown, and Anomalies (when available) to review detected deviations.
-
Open Billing > FinOps hub (or search the console for FinOps), and review Recommendations; each links to the resource and to the action that realizes the saving.
-
Under Notifications, subscribe the finance group to anomaly alerts.
With the gcloud CLI
gcloud services enable recommender.googleapis.com
PROJECT_ID=$(gcloud config get-value project)
# Idle VMs (zonal), idle external IP addresses (regional) and idle persistent disks (zonal)
gcloud recommender recommendations list --project="$PROJECT_ID" --location=us-central1-a \
--recommender=google.compute.instance.IdleResourceRecommender \
--format="table(name.basename(),description,primaryImpact.costProjection.cost.units)"
gcloud recommender recommendations list --project="$PROJECT_ID" --location=us-central1 \
--recommender=google.compute.address.IdleResourceRecommender
gcloud recommender recommendations list --project="$PROJECT_ID" --location=us-central1-a \
--recommender=google.compute.disk.IdleResourceRecommender
# Rightsizing of VM machine types
gcloud recommender recommendations list --project="$PROJECT_ID" --location=us-central1-a \
--recommender=google.compute.instance.MachineTypeRecommender
Recommendations are advisory: review each one, mark it as claimed, succeeded or dismissed with
gcloud recommender recommendations mark-claimed|mark-succeeded|mark-dismissed, and always check the resource’s
dependencies before deleting an "idle" one, since a rarely used resource may still be required.
Free trial and Always Free
Google Cloud has two separate free programs; the summary lives in Getting Started with Google Cloud and this section adds the details that matter for cost control.
| Program | Details |
|---|---|
Free trial |
New customers get a credit of about $300 valid for 90 days, applied before any charge, and it needs a self-serve billing account with a payment method for identity verification. The trial ends when the credit is used up or 90 days pass. Nothing is charged automatically: the account is paused until you activate a full paid account, and resources are deleted some days after the trial ends if you do not upgrade. During the trial some products are unavailable or limited: no GPUs, no Windows Server images, no Marketplace purchases, no quota increases and restrictions on other products. Each person gets the trial once. |
Always Free |
Monthly usage allowances for selected products that do not expire and are not limited to the trial. They include one small |
Two traps are worth knowing. First, allowances are per billing account (not per project), so creating more projects does
not multiply them. Second, the allowances are measured in specific units and regions; use a different region, a larger
shape or a feature outside the free SKU and the usage is billed at the normal price. Inside the trial, trial credits
are consumed before your card is charged, and credits show up as negative lines in the billing export (which is why
the earlier query sums the credits array). Some products also have their own free trials, for example a time-limited
free instance of Cloud SQL, AlloyDB or Spanner, documented on their pages; see
Managed Databases on Google Cloud. Credits from programs for startups or education are
separate from the free trial.
Hidden cost drivers
Most surprises come not from the big compute line but from components that bill by the hour whether or not traffic flows, or from storage and logs that grow silently. This qualitative table lists the ones that surprise newcomers most; look up current rates on the linked pricing pages.
| Driver | Why it costs money | What to do |
|---|---|---|
External IPv4 addresses |
In-use and reserved external IPv4 addresses are billed by the hour, and an address that is reserved but unattached is billed too. |
Prefer internal addressing, IAP and Private Google Access; release unused reservations (the idle-address recommender finds them). |
Cloud NAT gateways |
An hourly fee per gateway-and-VM plus data processing for every GiB through the gateway. |
One gateway per region, Private Google Access for Google APIs so that traffic skips the NAT. |
Load balancer forwarding rules |
Each forwarding rule is billed per hour plus data processed, even with no traffic. |
Share one load balancer between services with a URL map; delete test load balancers. |
Cloud Run minimum instances |
Instances kept warm are billed at an idle rate for the whole month. |
Use |
Soft-deleted bytes in Cloud Storage |
Soft delete is on by default with a retention window, and soft-deleted bytes are billed at the storage rate until the window ends. |
Lower the window or disable it on throwaway buckets (see Cloud Storage). |
Log ingestion and retention |
Cloud Logging charges for ingestion beyond a monthly free allocation per project, plus retention beyond the default period. |
Exclude noisy logs, route to cheaper sinks, and keep verbose debug logging off in production (see Monitoring and Observability). |
GKE cluster management fee |
Clusters bill an hourly management fee beyond the free cluster per account, in addition to nodes or Pods. |
Consolidate clusters, use namespaces, delete idle test clusters. |
Idle databases |
Cloud SQL, AlloyDB and Memorystore bill for the provisioned shape, storage and backups 24 hours a day. |
Stop or delete dev databases outside working hours and use scheduled start-stop. |
Extended support database fees |
Database versions past community end of life move into paid extended support with an extra per-vCPU charge. |
Upgrade engine versions on schedule (see Cloud SQL). |
Network egress |
Traffic leaving a zone, a region or Google Cloud is charged; inbound traffic is generally free. |
Keep chatty services in the same region and zone, use Cloud CDN, and compress responses. |
Unattached disks and old snapshots |
Persistent disks and snapshots bill per GiB-month after the VM is gone. |
Delete leftovers; use snapshot schedules with a retention period. |
Artifact Registry storage |
Old container images accumulate and bill per GiB-month. |
Add cleanup policies (see Container Registry and Builds). |
Where the Bookshelf money goes
The shape of the Bookshelf bill is more instructive than any single number. The table ranks the cost drivers qualitatively for a small production deployment, from usually largest to usually smallest, and says what controls each.
| Component | Typical share of the bill | Main levers |
|---|---|---|
Cloud SQL |
Largest, steady |
Right-size the tier, use a shared-core tier in dev, enable committed use discounts after a stable baseline, stop dev instances out of hours. |
Global external load balancer and Cloud CDN |
Medium, steady |
One shared load balancer; cache static assets so fewer bytes reach the origin; remove test forwarding rules. |
Cloud Run |
Medium, scales with traffic |
Request-based billing, a sensible CPU and memory, |
GKE Autopilot (if used instead of Cloud Run) |
Medium to large, steady |
Right-size resource requests, Spot Pods for batch, one cluster, flexible CUDs. |
Cloud NAT and external IPs |
Small, but constant |
Only for VMs that need outbound internet; reuse one gateway. |
Cloud Storage |
Small, grows over time |
Lifecycle rules, storage class by access pattern, short soft-delete window in dev. |
Firestore ( |
Small to medium |
Index discipline, TTL policies, a right-sized cache node. |
Cloud Logging, Monitoring, Trace |
Small until it is not |
Log exclusions, sampling, retention matching your needs. |
Pub/Sub, Secret Manager, Cloud Build, Artifact Registry |
Negligible to small |
Inside the free allowances at small scale; cleanup policies for images. |
Network egress |
Variable |
Same-region services, CDN, avoiding cross-region database calls. |
Label every component with app=bookshelf, component=… and env=…, export billing to BigQuery on day one and
create a monthly budget per environment, and the cost of each box in the diagram above becomes a single GROUP BY.
Quotas and limits
Billing and budget resources have their own limits, which are rarely reached but should be checked when you automate at scale:
-
Each project is linked to at most one billing account, and each billing account can have a limited number of projects linked by default; the quota is raised on request (and a new trial account has a low initial limit).
-
Budgets per billing account and thresholds per budget are capped; the budget documentation lists the current maximums.
-
A billing account can have a limited number of sub-accounts and a limited number of active commitments per region.
-
Service quotas (see Resource Hierarchy and Projects) are the only hard ceiling on consumption, so lowering a quota such as the number of CPUs in a region or the number of Cloud Run instances is a legitimate cost control.
Reference: Cloud Billing quotas and limits
Clean up
Cost tooling costs almost nothing, but remove what you created for experiments, in this order:
PROJECT_ID=$(gcloud config get-value project)
# Budgets
BUDGET_ID=$(gcloud billing budgets list --billing-account=000000-000000-000000 \
--filter="displayName=bookshelf-monthly" --format="value(name.basename())")
gcloud billing budgets delete "$BUDGET_ID" --billing-account=000000-000000-000000
# Budget notification topic (and any function subscribed to it)
gcloud pubsub topics delete budget-alerts
# Billing export dataset (disable the export in Billing > Billing export first)
bq rm -r -f -d "$PROJECT_ID:billing_export"
# Labels added for the experiments
gcloud run services update catalog-api --region=us-central1 --remove-labels=cost_center
gcloud projects update "$PROJECT_ID" --remove-labels=cost_center
# IAM bindings on the billing account
gcloud billing accounts remove-iam-policy-binding 000000-000000-000000 \
--member="group:finance@example.com" --role="roles/billing.viewer"
Disable the BigQuery export in the console before dropping the dataset, delete saved estimates in the pricing calculator
that you no longer need, and remove any service account used by a billing kill-switch function. A commitment cannot
be cancelled or deleted once purchased: check its term and price before buying, and note that it keeps billing even
if the project is deleted. Finally, the cleanest way to stop all charges from an experiment is to delete the throwaway
project (gcloud projects delete my-project-id), which stops its billing immediately and schedules the resources for
removal after the standard 30-day recovery window.
Reference: gcloud billing budgets delete,
Shutting down projects
Related pages
-
Getting Started with Google Cloud — the free trial and the first budget alert
-
Resource Hierarchy and Projects — projects, labels, tags and quotas
-
Identity and Access — IAM roles and groups
-
VPC Networking on Google Cloud — NAT, external IPs and egress
-
Architecture, Migration and Multicloud — the Well-Architected Framework and migration TCO
-
Cost Management and Support — the AWS equivalent
-
Cost Management — the Azure equivalent