Billing export runbook
Operational how-to for the BigQuery billing export: where it lives, how to query it, how to re-provision it, and the standing decisions that shape what it can and can't answer. For the cost figures this export produces, see GCP Cost Analysis § Billing Export — that page is the analysis, this page is the mechanics. Don't duplicate one into the other.
Where it lives
| Project | enkidu-488723 |
| Dataset | billing_export — location us-central1, not the US multi-region (see Decision: regional vs. multi-region below) |
| Standard usage cost table | gcp_billing_export_v1_01E02D_214CFC_4CC425 |
| Detailed/resource usage cost table | gcp_billing_export_resource_v1_01E02D_214CFC_4CC425 |
| Billing account | 01E02D-214CFC-4CC425 |
| Provisioning script | gcp_setup/19_billing_export.sh |
Both tables were enabled by hand on 2026-09-11 and cover the same underlying cost data at different grain (standard = per-SKU, detailed = per-resource).
Re-provisioning
gcp_setup/19_billing_export.sh is the authoritative source for provisioning, thresholds, and the worked query below — verify any claim on this page against it, not the other way around.
One step it cannot perform: there is no gcloud or API surface to configure a Cloud Billing export — gcloud billing exposes only accounts, budgets, and projects, and the Cloud Billing API has no export-configuration resource. Enabling the export itself is Console-only:
console.cloud.google.com/billing→ select billing account01E02D-214CFC-4CC425- Left nav → Billing export → BigQuery export tab
- Standard usage cost row → Edit settings → project
enkidu-488723, datasetbilling_export, locationus-central1→ Save - Detailed usage cost row → Edit settings, same project/dataset/location → Save
What the script covers instead (idempotent, safe to re-run):
- Dataset create-if-absent
- IAM assert-or-apply: dataset-scoped
READERand project-scopedroles/bigquery.jobUserfor the crew service account - A verification query that distinguishes "not yet loaded" from "still within initial backfill" from "misconfigured" from "pass" — see Backfill and propagation thresholds below for the exact cutoffs
Run it with ./gcp_setup/19_billing_export.sh. It exits non-zero for every state except a confirmed-loaded pass, including the two states that are expected and healthy early on (not-yet-loaded, still-within-backfill) — read the printed RESULT: line before treating a non-zero exit as a failure.
The no-backfill constraint
The export carries data from enablement (2026-09-11) forward only. This is a first-class property of the export, not a footnote: Google does not backfill a newly-enabled export with prior history, and there is no way to change that after the fact for data already outside the window.
A query scoped to "last month" (or any date range including August 2026) returns zero rows against a correctly configured export — this is expected, not a sign of misconfiguration. If a query like that is run inside 19_billing_export.sh's own verification step, it trips the script's zero-rows guard, because the script has no way to distinguish "queried outside the covered range" from "actually not loaded." Always scope queries to enablement forward:
SELECT
service.description AS service,
sku.description AS sku,
ROUND(SUM(cost), 2) AS total_cost
FROM `enkidu-488723.billing_export.gcp_billing_export_v1_01E02D_214CFC_4CC425`
WHERE DATE(usage_start_time) >= '2026-09-11'
GROUP BY service, sku
ORDER BY total_cost DESC
LIMIT 20For the results of running queries like this against the current coverage window, see GCP Cost Analysis § Querying the export.
Decision: regional vs. multi-region, and why it stands
The dataset was created at us-central1 (regional) rather than US (multi-region). Only multi-region datasets receive Google's previous-month backfill on enablement — so as built, this export holds no August 2026 data, permanently, on this dataset.
- Original rationale:
us-central1was chosen "to match the Cloud Run region for later joins." - That rationale does not hold. BigQuery dataset location only affects BigQuery-to-BigQuery joins — it has no bearing on joining against Cloud Run or any other regional GCP resource, because those aren't BigQuery tables to begin with. There is nothing else in this project to join
billing_exportagainst that the location choice would have protected. - The consequence is real and permanent on this dataset: no August 2026 billing data, and re-cutting to a
USmulti-region dataset would require creating a new dataset and re-enabling the export against it — this dataset can't be relocated in place. - It stands anyway, because its only consumer shipped without it. August was wanted for exactly one reason: Phase 6's criterion that resulting monthly spend be "stated from the billing export, not modeled." That criterion shipped 2026-09-18 (Phase 6b.2b-i, OBJ-3936, PR #3309) using a forward-only window (2026-09-11 → 2026-09-17) with pre-2026-09-11 figures explicitly labelled modeled, not measured. Nothing open in the v4.54 milestone consumes August — the one remaining task that needs a before/after comparison (6b.2b-ii, the staging-idling change) uses the already-measured September baseline as its "before," not August.
A future reader asking "why is there no August data, and why did nobody fix it" should be able to answer both from this section alone: the choice was a mistake, the consequence is permanent, and it was left as-is because the thing it would have unblocked shipped a different way.
Backfill and propagation thresholds (sourced)
19_billing_export.sh checks table age against two thresholds, in this order:
| Threshold | Value | Meaning |
|---|---|---|
INITIAL_PROPAGATION_HOURS | 6 | Table exists, 0 rows, younger than this → "not yet loaded," expected |
BACKFILL_WINDOW_HOURS | 120 | Table exists, 0 rows, between the two thresholds → "still within initial backfill," expected. Older than this → "misconfigured," a real failure |
Both are sourced directly from Google's own documentation, not estimated:
"it might take a few hours to start seeing Google Cloud cost data propagate to your BigQuery dataset" — export-data-bigquery-setup
"For the initial backfill of exported data, it might take up to five days for your retroactive Cloud Billing data to finish exporting" — export-data-bigquery
The old same-day (24h) "misconfigured" threshold was our own invented number (19_billing_export.sh:61, OBJ-3465 Task 1b.3), traced back to prose in that task's own issue description rather than to Google's documentation. It fired a false "misconfigured" alarm against an export that was actually healthy and still inside its normal initial-backfill window. It must not reappear — any future change to these thresholds should cite a Google source, not a round number that felt right.
Export rows settle over 1–3 days — a querying hazard
A day that reads as truncated when first queried can backfill to a complete 24-hour day over the following 1–3 days. Verify completeness by querying the export directly, not by trusting a prior note about it — this has already caused one real error in this milestone: docs/operations/gcp-costs.md recorded 2026-09-11 as truncated at 05:00Z (first usage row) against a 16:09Z enablement time. That was accurate when written (2026-09-13), but the day had since settled to a full 24 hours by the time the note was next read, and the stale note held a milestone gate two days past its actual opening.
Completeness check, as used in practice:
- A day is complete when hours 0–23 are all present in the export and the day is not the latest day in the table (the latest day is always truncated at its end by export lag — that's expected, not a bug).
- Cross-check both
gcp_billing_export_v1_01E02D_214CFC_4CC425andgcp_billing_export_resource_v1_01E02D_214CFC_4CC425against each other. They should agree on every daily total; if they don't, that's a real signal worth investigating before trusting either.
What this export does not have
- No August 2026 billing data. It is not available, not recoverable on the current
billing_exportdataset, and not pending — see Decision: regional vs. multi-region above for why. - Nothing before 2026-09-11 (enablement), on either table.
Verify against
gcp_setup/19_billing_export.sh— authoritative for provisioning, IAM, thresholds, and the worked querydocs/operations/gcp-costs.md§ Billing Export and § Coverage — the cost-analysis results this export has produced so far
Last updated: 2026-09-18