Quickstart
Before you start
You need four things.
| REQUIREMENT | WHY |
|---|---|
| An IaC definition | A repository containing databricks.yml with at least one target and one job or pipeline. |
| Python 3.9 or later | The CLI is a Python package. |
| The Databricks CLI | finowl diff and finowl guard run databricks bundle plan to read the deployed state. |
| A Finowl organization | An administrator invites you by email. Sign in once at portal.finowl.cloud before you use the CLI. |
Set up the product
Install the CLI
pip install finowl
finowl --versionFour pure-Python dependencies and no compiled extensions, so it installs in seconds on a CI runner. Install it into the same environment your pipeline uses. Version pinning and pipx in Install and overview.
Sign in
finowl auth loginA browser opens, you authenticate with your organization's identity provider, and the CLI stores the credential in a profile. If you belong to more than one organization it asks which one to use. On a machine with no browser, use finowl auth login --no-browser for the device flow.
Confirm what you are signed in as:
$ finowl auth describe
identity maya@acme.com
credential browser login
organization Acme Data Platform
host https://api.finowl.cloud
profile DEFAULT
expires in 8 h (2026-08-20 19:40)finowl auth login. A pipeline authenticates with its own OIDC identity and no stored secret — see Authentication.Register the project and word its workloads
Run init from the directory holding databricks.yml, naming the target you want to describe:
finowl init --target devIt registers the project on the platform, then walks every compute the bundle declares for that target — shared job clusters, inline task clusters, one serverless bucket per job, and each pipeline — and asks one question per compute: how much work runs on it per run?
1/3 job daily_ingest · compute main
shared cluster 'main': Standard_DS3_v2 × 4 workers
Workload [trivial/light/medium/heavy/intensive]: light
✓ light
2/3 job daily_ingest · compute serverless
serverless bucket: 2 task(s) with no declared compute
Workload [trivial/light/medium/heavy/intensive]: trivial
✓ trivial
3/3 pipeline customer_scd
pipeline cluster: Standard_DS4_v2 × 2 workers
Workload [trivial/light/medium/heavy/intensive]: medium
✓ mediumThe answers land in finowl.yml next to the bundle. Commit that file — every other command reads it and none of them ever prompt.
init once per target. Words are never inherited between environments, because how much data a compute chews through per run is exactly what differs between dev and prod. A bundle declaring dev, staging and prod needs three runs.Price it
finowl breakdown --target devYou get a monthly projection per resource — VM cost, DBU cost and the runtime hours they were derived from — plus totals for jobs, pipelines and warehouses. This is the number the board will show, and the run is already filed against the product.
Give the product a budget
A projection with nothing to measure it against is a fact, not a control. The budget comes from the web app, not from the CLI and not from a rule.
- Open the product on the board and run the estimator. Describe the project — volumes, pipelines, users — and it produces two figures: a P50 midpoint and a P80 that is sufficient in about 80% of modelled outcomes. If you already hold a figure from a statement of work or a spreadsheet, enter it by hand instead; it behaves identically from there.
- Commit the estimate. That single act freezes it as the agreement, records who agreed it and when, and sets the budget. Until it happens the product is flagged as having no committed budget and no cost rule can measure anything.
Full detail in Estimate and Commit.
Write a rule and put it in force
Rules live in the web app under Rules, and a rule does nothing until a policy carries it. Create two to start:
| RULE | SEVERITY | WHAT IT CHECKS |
|---|---|---|
| Budget exceeding | block | The product's projected total against that product's own budget. Takes no threshold — the figure is the budget. |
| Max cost increase ($) | warn | How much a single change adds per month. Measured against the change, not the total. |
Then create a policy, add both rules to it, and target the environments it applies to. A rule in no policy never runs. The rules page marks those Never runs so an unattached rule cannot be mistaken for enforcement.
Add the gate to CI
This is the whole Finowl integration for GitHub Actions. No API key: the pipeline proves what it is with the OIDC token GitHub signs for it, so there is no secret to rotate.
The DATABRICKS_* variables are a separate credential — they authenticate the Databricks CLI to your workspace, which breakdown and guard both need. OAuth M2M is shown here; every supported method is set out in Run it in CI.
name: cost
on:
pull_request:
push:
branches: [develop, staging, main]
env:
FINOWL_HOST: https://api.finowl.cloud
BUNDLE_TARGET: ${{ github.ref == 'refs/heads/main' && 'prod'
|| github.ref == 'refs/heads/staging' && 'staging'
|| 'dev' }}
jobs:
cost-guard:
runs-on: ubuntu-latest
permissions:
id-token: write # without this GitHub mints no OIDC token
contents: read
env:
# Databricks workspace auth — one of several methods; see "Run it in CI".
DATABRICKS_AUTH_TYPE: oauth-m2m
DATABRICKS_CLIENT_ID: ${{ secrets.DATABRICKS_CLIENT_ID }}
DATABRICKS_CLIENT_SECRET: ${{ secrets.DATABRICKS_CLIENT_SECRET }}
steps:
- uses: actions/checkout@v4
- uses: databricks/setup-cli@main
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install finowl==0.2.0
- name: Cost breakdown
run: finowl breakdown --target "$BUNDLE_TARGET"
- name: Cost diff
run: finowl diff --target "$BUNDLE_TARGET"
- name: Cost and governance guard
run: finowl guard --target "$BUNDLE_TARGET"Before this works, an administrator registers the pipeline once in Settings → CI connections: the repository, the issuer, and which organization its runs act for. Details in Run it in CI.
Verify the gate bites
Open a pull request that doubles a cluster. guard exits 1, the check goes red, and the failing rule names the resource and the figure. Revert it and the check goes green. That round trip is the whole product working.
What you have now
- A registered data product with a committed budget and a full run history.
- A projected monthly cost that updates on every pull request.
- A CI gate that fails a merge which would break the budget.
- A board where the product owner reads the same number the engineer saw, without anyone translating.
Next: word your other targets with finowl init --target staging, then connect an Azure subscription and a Databricks workspace so the projection sits next to what you were actually billed — see Actual cost.