dbt-costgate
The BigQuery cost gate for dbt pull requests.
Dry-run what changed, price the diff, and catch the $500-a-day model
before it merges — not on next month's bill.
Quick start · How it works · What you get · Where it fits · Pricing accuracy · Security · Roadmap · Contributing
[!NOTE] Working MVP.
dbt-costgate checkand the GitHub Action are implemented and tested. The PR-comment image below is an illustrative mock of the comment's design; the terminal output further down is real dbt-costgate output. See the usage guide and changelog.
The problem
On dbt + BigQuery teams, SQL changes merge with zero visibility into their cost impact. A changed join, a dropped partition filter, or a widened incremental window can multiply a model's bytes scanned — and the team finds out days later on the bill, or when finance escalates.
BigQuery's dry-run API returns the exact bytes a query would scan — for free, before running anything. dbt-costgate packages that into a first-class PR gate:
What you get on every PR
💻 The same check, in your terminal (real output)
$ dbt-costgate check --baseline path/to/main/manifest.json
dbt-costgate — region: US · on-demand $6.25/TiB · built-in table
fct_orders_daily (full-refresh): 68.20 MiB → 2.91 TiB +$18.19/run +$545.61/month (30 runs)
⚠ incremental — figure is the full-refresh scan
dim_customers (new): — → 412.50 MiB +$0.00/run +$0.07/month (30 runs)
GATE: FAIL
- fct_orders_daily: +$18.19/run exceeds $5.00
Pricing: US $6.25/TiB · built-in table (table 2026.07, verified 2026-07-23)
Estimates from BigQuery dry-run — nothing executed, no bytes billed, no SQL shown.
Or run it with no baseline at all for an instant local read of what your changed
models scan — and fail the run there on an absolute --max-usd-total /
--max-tib-total ceiling (no baseline required) — or get the full before/after
locally in one command with dbt-costgate check --against main (dbt-costgate compiles
main for you in a throwaway worktree). See the usage guide.
Quick start
pip install dbt-costgate
gcloud auth application-default login
dbt compile
dbt-costgate check
That's the entire local setup — no baseline, no CI, no config file. Add a baseline and thresholds when you want it to block a PR; see the usage guide.
Every release also ships a
wheel, an sdist, and SHA256SUMS if you'd rather pin to an artifact.
How it works
| Step | What happens | Cost to you |
|---|---|---|
| 1 · Find what changed | dbt's state:modified selector against a baseline manifest (your production artifacts), with a git-diff fallback |
free |
| 2 · Compile both versions | The baseline and PR-branch versions of each changed model | free |
| 3 · Dry-run each | BigQuery dryRun=true returns exact bytes scanned — executes nothing, reads no table data |
free |
| 4 · Price the diff | Region-aware on-demand rates; optionally × run frequency for $/month | free |
| 5 · Gate | Markdown PR comment, machine-readable JSON, policy-driven exit code (fail on a $ and/or % increase, or an absolute $/run or TiB/run ceiling) | free |
Where it fits
dbt-costgate is the preventive half of BigQuery cost control — it deliberately does not compete with the excellent retrospective tools:
| The question you're asking | Reach for |
|---|---|
| "What did our warehouse cost, by model / user / query?" | dbt-bigquery-monitoring |
| "What does the dbt platform estimate my models cost?" | dbt Cost Insights |
| "What is this PR about to do to our bill?" | dbt-costgate |
Accurate, transparent pricing
BigQuery on-demand rates differ by region — a gate that prices every byte at the US rate is silently wrong for half the world. dbt-costgate treats pricing accuracy as a feature:
-
🌍 Versioned per-region pricing table with a
last_verifieddate, auto-selected from your job's detected region. -
🧾 Every report discloses its math — region, rate, and rate source. Never a silent assumption:
region: US (multi-region) · on-demand $6.25/TiB · source: built-in table 2026.07
-
⚙️ Overridable —
pricing.regionto force a region,pricing.usd_per_tibfor negotiated or editions rates. -
⚠️ Honest limits, stated up front — under capacity/editions pricing, bytes scanned is a proxy signal, not your invoice; the 1 TiB/month free tier is not modeled by default.
Security model
This tool runs in CI next to warehouse credentials, so the design is deliberately boring:
| Threat | Design answer |
|---|---|
| Billable or data-reading queries | Dry-run only. The single warehouse interaction is jobs.insert with dryRun=true — free, executes nothing |
| Credential theft / mishandling | No credential surface. Auth delegates entirely to Application Default Credentials; in CI the documented path is keyless Workload Identity Federation. There are no credential flags to misuse |
| Compromised CI runner | Least privilege. BigQuery Job User + metadata read — no data access, no writes; docs ship the exact IAM setup |
| Malicious fork PRs | Fork-safe by default. Documented workflows use the pull_request trigger; fork PRs degrade to "no report", never to exposed secrets |
| Secrets templated into SQL | No compiled SQL in reports — model names, bytes, and dollars only; snippets are strictly opt-in |
| Phone-home | No telemetry. The only network call is to the BigQuery API |
Details in SECURITY.md · deeper design notes in docs/architecture.md.
Roadmap
-
dbt-costgate check— local (zero-setup) + CI diff, region-aware pricing, threshold gating - One-command local diff —
dbt-costgate check --against main(isolated git worktree) - GitHub Action wrapper with a sticky PR comment
- Absolute cost ceilings — gate on total
$/runorTiB/run, not just the increase (works without a baseline, so it gates local mode too) - Config- and macro-only change detection — catch a change that reaches a model without touching its
.sqlfile -
pre-commithook entry - Docker image on ghcr.io (GitLab CI–friendly)
- Live pricing (opt-in) via the Cloud Billing Catalog API
Non-goals
- Not a monitoring tool — retrospective observability belongs to dbt-bigquery-monitoring.
- BigQuery first — one warehouse done accurately beats three done approximately. Other warehouses come only once BigQuery is genuinely finished, and only where the cost model actually transfers.
- Never runs billable queries — features that require executing real queries are out of scope by design.
- No IDE/editor integration (for now).
Contributing · Security policy · Changelog · Code of Conduct · Apache-2.0 · NOTICE
Built by Dashan Richards — DCO sign-off required, hard invariants apply:
dry-run only · no credential handling · no telemetry
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dbt_costgate-0.7.1.tar.gz.
File metadata
- Download URL: dbt_costgate-0.7.1.tar.gz
- Upload date:
- Size: 76.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e6268849d1bf780daee122fc7248ea46b7ae3120d153a0c470ebdf1cca9bc771
|
|
| MD5 |
f846dd6673a211d570e10e6d553b9ead
|
|
| BLAKE2b-256 |
d416ee9c808e44f580ba7424a5a8f0a90b1bdbf4add25082ac3b2c2b9a219f76
|
Provenance
The following attestation bundles were made for dbt_costgate-0.7.1.tar.gz:
Publisher:
release.yml on Drichards124/dbt-costgate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dbt_costgate-0.7.1.tar.gz -
Subject digest:
e6268849d1bf780daee122fc7248ea46b7ae3120d153a0c470ebdf1cca9bc771 - Sigstore transparency entry: 2248769008
- Sigstore integration time:
-
Permalink:
Drichards124/dbt-costgate@752af2aad4689a4c594f16fbdef6cb370aafc3ce -
Branch / Tag:
refs/tags/v0.7.1 - Owner: https://github.com/Drichards124
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@752af2aad4689a4c594f16fbdef6cb370aafc3ce -
Trigger Event:
push
-
Statement type:
File details
Details for the file dbt_costgate-0.7.1-py3-none-any.whl.
File metadata
- Download URL: dbt_costgate-0.7.1-py3-none-any.whl
- Upload date:
- Size: 39.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
120a2e2315c2304f9dd6eb00757799b257eae6cda6c26c6ee599b36e62cd5488
|
|
| MD5 |
7db631f75b0a495daaf09f9479654a0f
|
|
| BLAKE2b-256 |
8683aa9b2ca62071e4ecddda311372f302e0688f68f4589f3f1b10f37ba5f232
|
Provenance
The following attestation bundles were made for dbt_costgate-0.7.1-py3-none-any.whl:
Publisher:
release.yml on Drichards124/dbt-costgate
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dbt_costgate-0.7.1-py3-none-any.whl -
Subject digest:
120a2e2315c2304f9dd6eb00757799b257eae6cda6c26c6ee599b36e62cd5488 - Sigstore transparency entry: 2248769344
- Sigstore integration time:
-
Permalink:
Drichards124/dbt-costgate@752af2aad4689a4c594f16fbdef6cb370aafc3ce -
Branch / Tag:
refs/tags/v0.7.1 - Owner: https://github.com/Drichards124
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@752af2aad4689a4c594f16fbdef6cb370aafc3ce -
Trigger Event:
push
-
Statement type: