finowl.yml reference
finowl init, holding two things: which project this bundle reports to, and how much work each of its computes does per run. Every other command reads it and none of them prompt.Where it lives
Always in the same directory as the bundle file. Move the bundle and both move together; point --bundle elsewhere and Finowl looks for finowl.yml beside it.
products/
sales/
databricks.yml
finowl.yml ← this bundle's project and workloads
marketing/
databricks.yml
finowl.yml ← a different projectA monorepo registers one project per bundle directory. There is no shared or root-levelfinowl.yml.
The file
version: 1
estimation:
id: 6f1c9a2e-4b13-4d0e-9f1a-2c7d8e5b0a34
name: reporting-q3
estimations:
targets:
dev:
jobs:
daily_ingest:
main: light
serverless: trivial
pipelines:
customer_scd: light
prod:
jobs:
daily_ingest:
main: heavy
serverless: light
pipelines:
customer_scd: intensiveThree top-level keys, and only three. Anything else is a validation error naming the key it did not expect.
version
Must be 1. It is the schema version of this file, and a future incompatible shape will use a different number rather than changing what 1 means.
estimation — the identity block
| KEY | REQUIRED | MEANING |
|---|---|---|
| id | yes | The project identifier issued at registration. A UUID. Every report is filed under it and nothing else. Never edit it by hand. |
| name | yes | The registered project name. Must match what the platform holds — change it with finowl rename, never by editing this line. |
| version | no | Accepted and ignored. It used to declare which estimate version the code built against, and it decided nothing: the budget a run is checked against is fetched by product id, so it moved the moment a new estimate was agreed. |
id stops every reporting command before it sends anything, and the platform rejects a report carrying an identifier it does not know. This is why copying a finowl.yml between bundles is the one mistake worth watching for: the copy is valid, and it points at the wrong project.estimation versus estimations
The two keys differ by one character, so a misplaced one is refused loudly rather than ignored. Putting id or name under estimations produces an error naming both blocks.
estimations.targets — one word per compute
Under each target, two optional sections. jobs maps a job name to its computes; pipelines maps a pipeline name straight to a word, because a pipeline is one compute.
estimations:
targets:
dev:
jobs:
daily_ingest:
main: light # a shared job cluster, by job_cluster_key
transform_task: medium # an inline cluster, by task_key
serverless: trivial # the job's serverless bucket
pipelines:
customer_scd: light| COMPUTE KEY | WHAT IT IDENTIFIES |
|---|---|
| <job_cluster_key> | A shared cluster declared in the job's job_clusters. |
| <task_key> | An inline cluster declared with new_cluster on a single task. |
| serverless | One bucket per job, collecting every task with no declared compute. Its word also sets the assumed serverless capacity. |
Warehouses never appear. They are natively sized and always priced at 730 hours a month.
The five words
| WORD | TYPICAL RUN | EXAMPLE |
|---|---|---|
| trivial | ~5 min | health checks, notifications, tiny metadata refreshes |
| light | ~20 min | incremental ingest of a few tables, small transforms |
| medium | ~1 h | a typical daily batch ETL joining several sources |
| heavy | ~3 h | large aggregations or feature engineering over 100s of GB |
| intensive | ~8 h | historical backfills, big model training, TB-scale joins |
Words are case-insensitive on read and written lowercase. Anything outside the five is a validation error listing the accepted words.
Targets do not inherit
A target is priced from its own section and nothing else. There is no fallback to another target and no shared defaults block.
The word stands for how much data a compute chews through per run, which is exactly what differs between dev and prod. A prod job inheriting dev's light does not report uncertainty — it prints a confident cost and passes or blocks a merge on it.
estimations.defaultsis no longer supported. A file carrying it is refused, empty or not, and is not migrated automatically: nothing can know whether prod deserved dev's numbers, and guessing that is the behaviour being removed.Run
finowl init --target <t> once per target your bundle declares, then delete the defaults block.Finowl owns this file
finowl initregenerates it in full. Hand-written comments are not preserved.- Ordering is stable — targets, jobs and computes are sorted — so a regeneration produces no spurious diff.
- Editing a word by hand is expected and supported. That is how you change an estimate;
initnever re-asks a compute that already has one. - Commit it. CI reads it and never prompts. A pipeline that cannot find it fails the workload gate with the complete list of what is missing.
The workload gate
Before any pricing call, breakdown, diff and guard check that every compute the bundle declares for this target has a word. Gaps are reported as one aggregated list rather than one error at a time.
Workload Error: Missing workload entries for 2 compute(s) in finowl.yml:
- job:daily_ingest → serverless
- pipeline:customer_scd
Accepted words: trivial, light, medium, heavy, intensive.
Run `finowl init` to detect computes and scaffold the entries.The gate runs before credentials are needed, so the complete to-do list is available offline.
Orphans
An entry for a compute the bundle no longer has is an orphan. Orphans are warnings, never errors — a rename must not break CI. Clear them with finowl init --prune.