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-000000 and 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.

graph LR PP["Payments profile
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 (roles/billing.creator)

Create new self-serve billing accounts. Granted on the organization.

Billing Account Administrator (roles/billing.admin)

Full control of one billing account: link and unlink projects, manage budgets, set IAM on the account, view costs.

Billing Account User (roles/billing.user)

Link projects to the billing account. Does not allow viewing costs or changing the account.

Billing Account Costs Manager (roles/billing.costsManager)

Manage budgets and cost-management settings such as exports and commitments, and view costs, without linking projects or editing IAM.

Billing Account Viewer (roles/billing.viewer)

View cost information, transactions and budgets, read-only.

Project Billing Manager (roles/billing.projectManager)

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

  1. 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.

  2. 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.

  3. To link or move a project, choose Actions next to the project, then Change billing, pick the target billing account and confirm.

  4. 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

  1. Open Billing, select the billing account, then Budgets & alerts, and choose Create budget.

  2. Name it bookshelf-monthly. Under Scope, choose the time range Monthly and select only the Bookshelf projects (and optionally a label filter such as app:bookshelf). Choose Next.

  3. Under Amount, choose Specified amount and enter the target, for example 200 in your billing currency, or choose Last month’s spend.

  4. Under Actions, set threshold rules for 50%, 90% and 100% of Actual spend and add 100% of Forecasted spend.

  5. 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());
        }
    }
}

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.projectManager on the project and roles/billing.user on 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

  1. Open the pricing calculator in a browser (it does not need an account) and choose Add to estimate.

  2. 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.

  3. Review the Estimated monthly cost and the per-service breakdown, choose Share or Save estimate and keep the link with the architecture documentation.

  4. 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

  1. Open Billing, select the billing account, then Commitments.

  2. 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.

  3. Review the price breakdown and the warning that the commitment cannot be cancelled, then confirm.

  4. 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

app

The application or product: bookshelf.

component

The deployable unit: catalog-api, orders-api, notifier, web.

env

The life-cycle stage: dev, stg, prod.

cost_center

The finance code charged back: cc1042.

owner

The team that answers for the resource: team-books.

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

  1. Open the resource in its console page, for example Cloud Run > catalog-api, and choose Edit and deploy new revision or the Labels tab.

  2. Add the label pairs app, component, env and owner, then save or deploy.

  3. For a project, open IAM & Admin > Settings (or Manage resources), choose the project and Labels, and add app:bookshelf and env:prod.

  4. 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

  1. Create the destination dataset first: open BigQuery, choose the project, Create dataset, name it billing_export, pick the location (for example US) and Create dataset.

  2. Open Billing, select the billing account, then Billing export.

  3. On the Standard usage cost tab, choose Edit settings, select the project and the billing_export dataset and choose Save. Repeat on the Detailed usage cost and Pricing tabs as required.

  4. 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

  1. Open Billing > Reports, set the time range, choose Group by > Service and Filters > Projects for bookshelf-prod, and look for unexpected jumps.

  2. Open Billing > Cost table for a downloadable breakdown, and Anomalies (when available) to review detected deviations.

  3. 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.

  4. 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 e2-micro VM in selected US regions, a few GiB of Cloud Storage in selected regions, free monthly Cloud Run requests and compute time, a monthly allowance of BigQuery queries and storage, Pub/Sub, Secret Manager, Firestore and Cloud Build allowances, and no cluster management fee for one GKE Autopilot or zonal cluster per billing account.

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 min-instances of 0 outside production; set max-instances to cap runaway scaling.

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 bookshelf-catalog (and AlloyDB if used)

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 catalog-api and orders-api

Medium, scales with traffic

Request-based billing, a sensible CPU and memory, max-instances, and a small min-instances only for the user-facing path.

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 bookshelf-web and bookshelf-uploads

Small, grows over time

Lifecycle rules, storage class by access pattern, short soft-delete window in dev.

Firestore (orders) and Memorystore

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.

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.