Find Your Most Expensive GCP Resources: BigQuery + gcloud

The fastest way to find which specific GCP resources are driving your bill is the detailed BigQuery billing export. It includes a resource.name field on every line item, so you can rank individual VMs, disks, buckets, and databases by exact cost. This page gives you the SQL queries, gcloud commands, and workflow to go from “Compute Engine costs too much” to “this specific VM costs $1,400 and is idle.”

Console billing reports, Active Assist recommendations, and gcloud CLI checks each play a supporting role. Reports give you a quick visual breakdown. Active Assist flags idle resources automatically. gcloud lets you list unattached disks and reserved IPs in seconds. But the detailed export is the single source of truth for per-resource cost.

By the end of this page you will be able to: query your billing data for the most expensive resources across all services, drill into SKU-level costs, find idle waste with gcloud, and decide what action to take on each resource.

In plain English

Think of your GCP bill like a restaurant receipt that only shows the total per course: “starters $40, mains $120, drinks $60.” That tells you mains cost the most, but it does not tell you whether the $120 came from one person ordering lobster or from six people ordering pasta. Service-level cost is the per-course total: “Compute Engine cost $3,000.” Resource-level cost is the itemised line: “VM my-old-analytics-vm cost $1,800 of that $3,000 because it was left running at an oversized machine type.” Resource-level cost is what makes a bill actionable.

A few terms you will see throughout this page:

  • resource.name – a field in the detailed billing export that contains the specific name of the resource (the VM name, bucket name, disk name) that generated the charge.

  • SKU – a Stock Keeping Unit. In GCP billing, each SKU represents a specific type of usage, such as “N2 Instance Core running in Americas” or “SSD backed PD Capacity.” Drilling into SKUs tells you whether a high cost comes from CPU, memory, disk, or network egress.

  • Labels – key-value pairs you attach to resources (e.g. team=backend, env=production) so you can filter and group costs by team, environment, or application. See Labels and Tags for full details.

  • Rightsizing – changing a resource to a size that matches its actual usage. A VM running at 5% CPU on an n2-standard-16 can often be rightsized to an n2-standard-4 with no performance impact.

What you need before you start

  • Billing account access. You need the Billing Account Viewer role (or higher) to see cost data and export settings.

  • Detailed billing export enabled. In the GCP Console, go to Billing → Billing export → Detailed usage cost and enable it. See Cost Breakdown Reports for the full export setup walkthrough.

  • BigQuery dataset access. The export writes to a BigQuery dataset. You need BigQuery Data Viewer on that dataset to run queries.

  • gcloud CLI or Cloud Shell (optional). Needed for the idle resource commands. Cloud Shell has gcloud pre-installed.

Standard vs detailed export

There are two billing exports. The standard export shows costs grouped by service and project. The detailed export adds the resource.name field that tells you which specific resource generated each charge. Every query on this page requires the detailed export. If you only have the standard export enabled, the per-resource queries will not work.

How finding expensive resources works

The process follows four steps, whether you are investigating a bill spike or doing a routine FinOps review. Think of it like triage in an emergency room: first identify the most critical patient, then diagnose the specific problem, then decide on treatment.

  1. Rank resources by spend. Query the detailed billing export to find the top resources by total cost this month. This tells you where the money is going.

  2. Break spend down by SKU. For the highest-cost resources, drill into the SKU level to see whether the cost is driven by CPU, memory, disk, or egress.

  3. Check whether the resource is idle or overprovisioned. Use Cloud Monitoring metrics or Active Assist to see actual utilisation. A VM running at 5% CPU is a different problem than a VM running at 80%.

  4. Decide the action. Depending on what you find: resize the resource, delete it, change its pricing model, improve its labels for tracking, or leave it alone because the cost is justified.

Fastest ways to find expensive resources

Detailed BigQuery billing export

When to use it: Whenever you need a precise, per-resource cost ranking, trend analysis, or custom cost reporting across all services.

What it is good at: Full SQL flexibility. You can filter by service, SKU, label, project, date range, and resource name. It is the only method that gives exact per-resource cost for every service that populates resource.name.

What it cannot show: Real-time data. The export has a delay of a few hours to 24 hours. Services that do not populate resource.name will not appear in per-resource queries. Running large queries against the export itself also costs money. See BigQuery Cost Optimisation to keep query costs low.

Billing reports in the Console

When to use it: For a quick visual overview of cost by service, SKU, project, or label, without writing SQL.

What it is good at: Spotting trends and spikes at a glance. The Console shows daily and monthly views with filters for service, project, and label. No setup required beyond billing account access.

What it cannot show: Individual resource names. Console reports show cost at the service and SKU level, but not which specific VM or bucket is responsible. For that, you need the BigQuery export.

Active Assist and Recommender

When to use it: For automatic detection of idle and overprovisioned resources. Navigate to Recommender → Cost Optimization in the Console.

What it is good at: Surfacing specific resource names with estimated monthly savings. It flags idle VMs, oversized VMs, unattached disks, and idle Cloud SQL instances based on actual utilisation data.

What it cannot show: A complete cost ranking across all resources. It only flags resources that meet its idle or overprovisioned thresholds. High-cost resources with legitimate utilisation will not appear.

gcloud checks for idle waste

When to use it: For a fast command-line sweep of common waste: unattached disks, reserved IPs, stopped VMs, and old snapshots.

What it is good at: Listing orphaned resources in seconds with no BigQuery setup. The commands in the next sections are copy-paste-ready.

What it cannot show: The actual dollar cost of each resource. gcloud shows resource metadata (size, type, status) but not billing amounts. Cross-reference with the BigQuery export for exact costs.

Detailed BigQuery queries

All queries below use the detailed billing export table. Replace PROJECT, DATASET, and BILLING_ACCOUNT_ID with your values.

Table name pattern

The detailed export table follows this naming convention:
PROJECT.DATASET.gcp_billing_export_resource_v1_BILLING_ACCOUNT_ID
Notice the resource in the name. The standard export table uses v1 without resource. If your query returns no results, double-check that you are using the correct table.

Top 20 most expensive resources this month

This is the starting query. It ranks every resource across all services by total cost for the current month.

SELECT
  resource.name AS resource_name,
  service.description AS service,
  resource.type AS resource_type,
  ROUND(SUM(cost), 2) AS total_cost
FROM `PROJECT.DATASET.gcp_billing_export_resource_v1_BILLING_ACCOUNT_ID`
WHERE
  DATE(usage_start_time) >= DATE_TRUNC(CURRENT_DATE(), MONTH)
  AND cost > 0
  AND resource.name IS NOT NULL
GROUP BY resource_name, service, resource_type
ORDER BY total_cost DESC
LIMIT 20;

How to read the result: The top rows are your most expensive individual resources. The service column tells you which GCP service owns the resource, and resource_type tells you whether it is a VM, disk, bucket, etc. If one resource dominates the list, drill into its SKUs next.

Most expensive Compute Engine VMs

Compute Engine is often the largest line item. This query isolates VM instances specifically.

SELECT
  resource.name AS vm_name,
  location.region AS region,
  ROUND(SUM(cost), 2) AS total_cost
FROM `PROJECT.DATASET.gcp_billing_export_resource_v1_BILLING_ACCOUNT_ID`
WHERE
  DATE(usage_start_time) >= DATE_TRUNC(CURRENT_DATE(), MONTH)
  AND service.description = 'Compute Engine'
  AND resource.type = 'compute.googleapis.com/Instance'
GROUP BY vm_name, region
ORDER BY total_cost DESC
LIMIT 20;

How to read the result: Each row is a single VM. If one VM is significantly more expensive than the rest, check its machine type and CPU utilisation. It may be a candidate for rightsizing or for a committed use discount.

Most expensive disks and snapshots

Persistent disks and snapshots accumulate cost quietly, especially when they are unattached or outdated.

SELECT
  resource.name AS disk_or_snapshot,
  sku.description AS sku,
  ROUND(SUM(cost), 2) AS total_cost
FROM `PROJECT.DATASET.gcp_billing_export_resource_v1_BILLING_ACCOUNT_ID`
WHERE
  DATE(usage_start_time) >= DATE_TRUNC(CURRENT_DATE(), MONTH)
  AND service.description = 'Compute Engine'
  AND (resource.type LIKE '%Disk%' OR resource.type LIKE '%Snapshot%')
GROUP BY disk_or_snapshot, sku
ORDER BY total_cost DESC
LIMIT 20;

How to read the result: The SKU column tells you whether the cost is from SSD PD, standard PD, or snapshot storage. Cross-reference disk names with the gcloud idle resource commands below to find disks that are unattached.

Most expensive Cloud Storage buckets

Storage buckets can become expensive when they hold large volumes of data in the wrong storage class or generate heavy egress. See Storage Cost Optimisation for strategies.

SELECT
  resource.name AS bucket_name,
  sku.description AS sku,
  ROUND(SUM(cost), 2) AS total_cost
FROM `PROJECT.DATASET.gcp_billing_export_resource_v1_BILLING_ACCOUNT_ID`
WHERE
  DATE(usage_start_time) >= DATE_TRUNC(CURRENT_DATE(), MONTH)
  AND service.description = 'Cloud Storage'
GROUP BY bucket_name, sku
ORDER BY total_cost DESC
LIMIT 20;

How to read the result: The SKU column shows whether the cost is from storage capacity, Class A operations (writes), Class B operations (reads), or egress. A bucket with high egress cost may point to an architecture issue. See Network Egress Costs to understand what you pay and what is free.

Top egress SKUs across all services

Egress (data leaving GCP) is a common surprise cost. This query finds the most expensive egress charges regardless of service.

SELECT
  service.description AS service,
  sku.description AS sku,
  ROUND(SUM(cost), 2) AS total_cost
FROM `PROJECT.DATASET.gcp_billing_export_resource_v1_BILLING_ACCOUNT_ID`
WHERE
  DATE(usage_start_time) >= DATE_TRUNC(CURRENT_DATE(), MONTH)
  AND LOWER(sku.description) LIKE '%egress%'
  OR LOWER(sku.description) LIKE '%download%'
GROUP BY service, sku
ORDER BY total_cost DESC
LIMIT 20;

How to read the result: The SKU description tells you the destination region or tier. “Download Worldwide Destinations” is internet egress. Inter-region egress within GCP is cheaper but can still add up at scale.

Cost breakdown by SKU within a service

When a single service shows high cost, this query reveals exactly which types of usage are driving it. Change the service filter to investigate any service.

SELECT
  sku.description AS sku,
  ROUND(SUM(cost), 2) AS total_cost,
  ROUND(SUM(cost) / SUM(SUM(cost)) OVER () * 100, 1) AS pct_of_service_cost
FROM `PROJECT.DATASET.gcp_billing_export_resource_v1_BILLING_ACCOUNT_ID`
WHERE
  DATE(usage_start_time) >= DATE_TRUNC(CURRENT_DATE(), MONTH)
  AND service.description = 'Compute Engine'
GROUP BY sku
ORDER BY total_cost DESC;

How to read the result: The pct_of_service_cost column shows what share of the service’s total cost each SKU represents. Common Compute Engine SKUs to watch for:

  • “N2 Instance Core running in Americas” – vCPU hours for N2 VMs
  • ”SSD backed PD Capacity” – SSD persistent disk storage (~$0.17/GB/month)
  • “Download Worldwide Destinations (excluding Asia & Australia)” – internet egress
  • ”Cloud SQL for PostgreSQL: Zonal - 2 vCPU” – always-on SQL instance compute

Cost per team and environment using labels

If your resources are labeled with team and env keys, this query shows cost attribution by team and environment.

SELECT
  (SELECT value FROM UNNEST(labels) WHERE key = 'team') AS team,
  (SELECT value FROM UNNEST(labels) WHERE key = 'env') AS environment,
  ROUND(SUM(cost), 2) AS total_cost
FROM `PROJECT.DATASET.gcp_billing_export_resource_v1_BILLING_ACCOUNT_ID`
WHERE DATE(usage_start_time) >= DATE_TRUNC(CURRENT_DATE(), MONTH)
GROUP BY team, environment
HAVING team IS NOT NULL
ORDER BY total_cost DESC;

How to read the result: Each row shows a team-environment combination and its total cost. If the team column is NULL for a significant portion of spend, that means unlabeled resources. Labeling them should be a priority.

Finding idle and unattached resources with gcloud

Resources that are provisioned but unused are pure waste. These gcloud commands surface the most common sources. For a complete cleanup workflow, see Cleaning Up Unused Resources.

# Find VMs that are stopped but still incurring disk costs
gcloud compute instances list \
  --filter="status=TERMINATED" \
  --format="table(name,zone,status,lastStartTimestamp)"

# Find unattached persistent disks (no VM using them)
# SSD PD costs ~$0.17/GB/month — a 500 GB unattached SSD costs ~$85/month
gcloud compute disks list \
  --filter="users.len()=0" \
  --format="table(name,zone,sizeGb,type,lastDetachTimestamp)"

# Find reserved static IPs not attached to any VM or forwarding rule
# Reserved-but-unattached IPs cost ~$0.01/hour each (~$7.30/month)
gcloud compute addresses list \
  --filter="status=RESERVED" \
  --format="table(name,region,status,address)"

# Find old snapshots accumulating storage costs
gcloud compute snapshots list \
  --sort-by=creationTimestamp \
  --format="table(name,diskSizeGb,creationTimestamp,storageBytes)"
The silent cost trap

When you delete a VM, its persistent disk and reserved IP are not deleted automatically. A 500 GB SSD disk costs ~$85/month sitting unattached indefinitely. Ten forgotten disks across a team can quietly add up to $10,000/year. Run these gcloud commands at least quarterly.

Spotting under-utilised VMs with Cloud Monitoring

A VM can be expensive without being idle. Low CPU utilisation over a sustained period means the machine type is larger than necessary. It is like paying for a 10-seat van when you only ever carry two passengers. Use Cloud Monitoring to check. Replace PROJECT_ID with your project and adjust the date window as needed.

# Check average CPU utilisation for all VMs over the last 7 days
# Replace START_DATE and END_DATE with your date range in ISO 8601 format
# Example: START_DATE=2026-03-20T00:00:00Z  END_DATE=2026-03-27T00:00:00Z
START_DATE=$(date -u -d '7 days ago' +%Y-%m-%dT00:00:00Z)
END_DATE=$(date -u +%Y-%m-%dT00:00:00Z)

gcloud monitoring time-series list \
  --project=PROJECT_ID \
  --filter='metric.type="compute.googleapis.com/instance/cpu/utilization"' \
  --aggregation-align-period=86400s \
  --aggregation-per-series-aligner=ALIGN_MEAN \
  --interval-start-time="$START_DATE" \
  --interval-end-time="$END_DATE"
Quick alternative

If you do not want to run gcloud commands, open the Compute Engine VM list in the Console and look for the Recommendations column. GCP shows rightsizing suggestions directly there, with the recommended machine type and estimated savings.

A VM with average CPU below 10% over a full week is a strong candidate for rightsizing.

Billing report vs detailed export vs Active Assist

Each method covers different ground. Use this table to decide which tool to reach for in a given situation.

Console billing reportDetailed BigQuery exportActive Assist
Best use caseQuick visual overview of cost trendsPrecise per-resource cost ranking and analysisAutomatic detection of idle and overprovisioned resources
Level of detailService, SKU, project, labelService, SKU, project, label, plus individual resource nameSpecific resource recommendations with savings estimates
StrengthsNo setup, fast, visualFull SQL flexibility, exact per-resource cost, custom reportingZero-effort, actionable, includes savings estimate
LimitationsNo per-resource names, no custom queriesRequires export setup, query cost, hours of delayOnly flags resources that meet idle/overprovisioned thresholds
When to chooseDaily monitoring, quick spike investigationMonthly FinOps review, deep investigation, chargeback reportingRoutine cleanup sweeps, quick wins

In practice, most teams use all three. Console reports for daily monitoring, BigQuery for monthly reviews and deep dives, and Active Assist for low-hanging fruit between reviews.

When to use this page

  • Unexpected bill spike. Your bill jumped and you need to find exactly which resource is responsible, fast.

  • Monthly FinOps review. Part of a regular cost optimisation strategy where you rank resources by spend and take action on the top items.

  • Pre-rightsizing investigation. Before rightsizing VMs, you need to know which VMs cost the most and which are under-utilised.

  • Before cleanup. Before running a cleanup sweep to remove unused resources, identify which ones are costing real money so you can prioritise.

  • Before buying commitments. Before purchasing committed use discounts, you should know which resources have stable, sustained usage. Use the queries here to verify consistent spend on specific resource types. See GCP Pricing Models for how CUDs work.

Common beginner mistakes

  1. Using the standard billing export instead of the detailed export. The standard export does not include the resource.name field. You will get service-level totals but no per-resource breakdown. Enable the detailed export under Billing → Billing export → Detailed usage cost.

  2. Looking only at service-level costs. “Compute Engine cost $2,000 this month” is not actionable. You need to find which specific VM, disk, or IP is responsible. That is the difference between knowing you have a problem and knowing how to fix it.

  3. Not labeling resources before trying cost attribution. Without labels, figuring out which team owns an expensive resource requires manual investigation. Retroactive labeling is slow and often impossible for old or shared resources. Enforce labels at creation time using organisation policies or Terraform modules.

  4. Ignoring unattached disks, snapshots, and reserved IPs. When a VM is deleted, its disk and reserved IP often remain. A 500 GB SSD persistent disk costs ~$85/month even when unattached. Run the gcloud commands above at least quarterly.

  5. Finding the expensive resource but not acting on it. The investigation is only valuable if it leads to an action: resize, delete, change pricing model, or improve labels. If you are not sure what to do, start with the next section.

What to do after you identify the expensive resource

Once you know which resource is costing the most, the next step depends on what you find:

  • Overprovisioned compute: Rightsize the VM to a smaller machine type that matches actual utilisation.

  • Idle or unused resources: Delete or shut down unattached disks, stopped VMs, unused snapshots, and reserved IPs.

  • High egress costs: Review your architecture for unnecessary cross-region traffic or internet egress. See Network Egress Costs for what is free and what is not.

  • Missing labels: Add labels so you can track cost by team, environment, and application going forward.

  • No budget alerts: Set up billing budgets and alerts so you catch the next spike before it becomes a surprise.

  • Stable, high-cost workloads: If the cost is legitimate and the resource runs consistently, check whether a committed use discount or sustained use discount would reduce the bill. See GCP Pricing Models.

Start with the biggest line item

Do not try to optimise everything at once. Fix the single most expensive resource first. One well-targeted action (deleting an idle $200/month VM, rightsizing a $1,400/month machine) often saves more than a dozen small tweaks combined.

Frequently asked questions

Do I need the detailed billing export or is the standard export enough?

You need the detailed export. The standard export shows costs grouped by service and project but does not include the resource.name field. Without resource.name, you cannot see which specific VM, disk, or bucket is driving the cost. Enable the detailed export under Billing > Billing export > Detailed usage cost in the GCP Console.

Can I find expensive resources without BigQuery?

Partially. The Billing Reports page in the Console shows service-level and SKU-level costs, and Active Assist flags idle VMs, disks, and IPs with estimated savings. For quick cleanup of obvious waste, gcloud commands can list unattached disks, reserved IPs, and stopped VMs. But for a precise per-resource cost ranking across all services, the BigQuery detailed export is the only built-in option.

Why are some resource names missing from the billing export?

Not every GCP service populates the resource.name field. Some services report only at the project or SKU level. If you enabled the detailed export recently, older rows will not have resource names retroactively. Also, shared-tenancy services like Cloud Functions or Cloud Run may group costs by service rather than individual function or revision.

How long does billing export data take to appear in BigQuery?

Billing data typically appears in BigQuery within a few hours of the usage occurring, but Google states it can take up to 24 hours. After first enabling the export, you may need to wait a full day before any data appears. The export is not real-time, so for same-day cost spikes, check the Billing Reports page in the Console first.

What should I do after finding the most expensive resource?

First, determine whether the cost is expected or not. Check CPU and memory utilisation in Cloud Monitoring. If the resource is idle, delete it or shut it down. If it is under-utilised, rightsize it to a smaller machine type. If the cost is legitimate but high, check whether a committed use discount or different pricing model would reduce the bill. Add labels so you can track the cost going forward.

Last verified: 27 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.