Get started

Install Freshet, plan your first backfill, and learn the loop every setup follows: plan, run, then commit.

Install

One binary, freshet, with no runtime dependencies. Pick whichever fits where it will run.

a prebuilt binary (Linux x86_64; macOS and Windows builds are on the release page)
$ curl -sSL https://github.com/lucheeseng827/freshet/releases/download/v0.1.0/freshet-v0.1.0-x86_64-unknown-linux-musl.tar.gz | tar xz
$ sudo mv freshet-v0.1.0-x86_64-unknown-linux-musl/freshet freshet-v0.1.0-x86_64-unknown-linux-musl/freshet-serve /usr/local/bin/
$ freshet --version
freshet 0.1.0
or from crates.io, or as a container
$ cargo install freshet-cli --locked
$ docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/work" mancube/freshet --version
freshet 0.1.0

The image runs as an unprivileged user, so pass --user as above when it should write state.json into a directory you own. Every release lists its binaries and checksums on GitHub. Python users can pip install freshet-planner for the planner in-process.

Your first plan

Freshet reads a directory where every .sql file is one model, named after the file. Make three: a staging model and two marts that read it.

a three-model project
$ mkdir -p models
$ echo 'select a, b from raw'   > models/stg.sql
$ echo 'select a as x from stg' > models/mart_a.sql
$ echo 'select b as y from stg' > models/mart_b.sql
$ echo '{"raw":["a","b"]}'                            > models/sources.json
$ echo '{"stg":"incremental","mart_a":"incremental"}' > models/materializations.json

sources.json names the columns of tables you read but don't build. materializations.json marks which models are incremental; everything else is a table. Now ask what must rebuild. Nothing has been built yet, so everything does:

plan, then commit
$ freshet plan --project models --state state.json --commit
backfill plan (3 model(s), topological order):
  stg                      full                         (directly changed)
  mart_a                   full                         (directly changed)
  mart_b                   full                         (directly changed)
rebuild work: 3.000 CU planned, 0.000 CU (0%) avoided — rebuilding everything downstream of the change whole would be 3.000 CU
committed state -> state.json
$ freshet plan --project models --state state.json
nothing to backfill — state is up to date.

--commit saved each model's fingerprint in state.json, so the second plan has nothing to do. Late data arrives for one day of stg; restate that partition and the plan follows lineage from it:

restate one day
$ freshet restate --project models --model stg --partitions 2026-06-20
backfill plan (3 model(s), topological order):
  stg                      partitions[2026-06-20]       (directly changed)
  mart_a                   partitions[2026-06-20]       (downstream of stg via [x])
  mart_b                   full                         (downstream of stg via [y])
rebuild work: 1.200 CU planned, 1.800 CU (60%) avoided — rebuilding everything downstream of the change whole would be 3.000 CU

Read a plan

PartWhat it tells you
OrderModels are listed in topological order: run them top to bottom.
full / partitions[…]Rebuild the whole model, or only these partitions. Only incremental models can be partition-scoped; mart_b is a table, so it rebuilds in full.
Reasondirectly changed, or downstream of X via [columns]: the columns through which the change reaches this model.
Rebuild workThe plan priced in compute units (CU). One CU is one full rebuild of one table; a partition is 0.1; a view 0.05. The "avoided" figure is against rebuilding everything downstream of the change whole.
WidenedWhen a partition-scoped write can't be honoured, the plan rebuilds more and says why. It never rebuilds less than it must.

The one rule: commit after the run

Freshet plans; something else runs the plan. State must record only what was actually rebuilt, so the loop is always the same three steps:

  1. Plan with freshet plan (add --json for machines, --sql for statements).
  2. Run it: dbt, dagron, Airflow, or the rendered SQL.
  3. Commit with freshet plan --commit, only if the run succeeded.

Never commit before the run finishes. A commit records the models as up to date; if the run then fails, the next plan skips work that was never done.

Plan a backfill covers real projects: partitions, SQL for your warehouse, budgets, and dbt. Run it on your cluster covers step 2.