DBT_PROJECT.YML
Every DVT project is a dbt project, and dbt_project.yml is its front door — the file that names the project and tells the toolkit where everything lives. DVT reads it exactly as dbt does: same keys, same meanings, same defaults.
If you already have a dbt project, you already have this file — DVT runs it unchanged. And nothing DVT-specific ever goes in it; that boundary is enforced, not just promised (more below).
A REAL ONE, FIRST
This is the dbt_project.yml that dvt init sales_dwh writes — a complete, working configuration:
# sales_dwh — a DVT project (dbt-compatible)
name: 'sales_dwh'
version: '1.0.0'
profile: 'sales_dwh'
model-paths: ["models"]
seed-paths: ["seeds"]
test-paths: ["tests"]
macro-paths: ["macros"]
target-path: "target"
clean-targets: ["target", "dbt_packages"]
models:
sales_dwh:
+materialized: view
# DVT's disclosed telemetry is the one channel: dbt's own
# anonymous usage stats stay off.
flags:
send_anonymous_usage_stats: falseRead it top to bottom and you have the whole shape of the file: who the project is (name, version, profile), where its files live (the *-paths keys), and how its models build by default (the models: tree). Every line is standard dbt — you could feed this file to plain dbt and it would parse without complaint.
In plain dbt, dbt init walks you through adapter prompts and you need a warehouse before your first run. dvt init writes this file with no prompts and points the profile at a local DuckDB file — so dvt build is green seconds later, no warehouse involved.
THE IDENTITY TRIO: NAME, PROFILE, VERSION
name is how the project refers to itself: lowercase letters, digits and underscores. It matters more than it looks — it is the key that namespaces your models: and seeds: config trees below.
profile names the entry in profiles.yml that holds this project's connections and default target. The scaffold keeps project name and profile name equal, which is a pleasant convention — but any profile name works, and several projects may share one profile.
version is your own label for the project — any string, purely informational. Bump it when you feel like it; nothing reads it to make decisions.
name: 'sales_dwh' # namespaces the models: tree below profile: 'sales_dwh' # → the entry in profiles.yml version: '1.0.0' # yours; informational
THE PATH KEYS: WHERE DVT LOOKS FOR THINGS
Each *-paths key takes a listof folders, relative to this file. The defaults are dbt's defaults, so most projects never write more than the scaffold does — but every one of them is yours to move:
model-paths: ["models", "shared/models"] # SQL + .py models, sources.yml seed-paths: ["seeds"] # csv / parquet / json / jsonl snapshot-paths: ["snapshots"] # snapshot definitions test-paths: ["tests"] # singular data tests analysis-paths: ["analyses"] # compiled-not-run SQL macro-paths: ["macros"] # Jinja macros
Because these are lists, a monorepo can point model-paths at several folders at once. Everything found under them joins one project and one DAG — exactly as in dbt.
MODELS: — CONFIGURE WHOLE BRANCHES AT ONCE
The models:tree sets configs for entire folders instead of repeating them in every file. Under your project's name, nested keys mirror your folder structure, and keys prefixed with + are configs applied to everything beneath them:
models:
sales_dwh:
+materialized: view # project-wide default
staging:
+tags: ["staging"] # everything in models/staging/
marts:
+materialized: table # everything in models/marts/
+schema: marts
finance:
+materialized: f_table # cross-engine marts — DVT's federated buildPrecedence is dbt's rule, unchanged: a config() block in the model itself wins, then the deepest matching folder, then the project level. Set the broad default once up here and override per model only where it matters.
The tree speaks DVT's materializations too. In plain dbt, +materialized chooses between table, view, incremental and ephemeral; in DVT, f_table and f_incremental are equally valid values — so one line federates a whole folder of cross-engine models.
SEEDS: — PIN A COLUMN'S TYPE WHEN INFERENCE GUESSES WRONG
Seeds load with inferred column types, which is almost always fine. When an engine is stricter than the inference — Databricks in ANSI mode rejecting 1-Mar-15-style dates is the classic — pin the column with +column_types, keyed by the seed's file name:
seeds:
sales_dwh:
raw_orders: # seeds/raw_orders.csv
+column_types:
ordering_date: string # load as-is; cast downstreamThis is dbt's own seed-typing syntax, honored on DVT's load path — both the +column_types and bare column_types spellings work, however deep the seed sits in the tree. And a note on formats: the seeds folder holds csv, parquet, json and jsonl files — those four, exactly.
VARS: — ONE PLACE FOR PROJECT-WIDE VALUES
vars: declares values your models can read anywhere with {{ var('...') }} — cutoff dates, feature switches, lists to loop over. Declared here, overridable per run:
vars: start_date: '2024-01-01' payment_methods: ['credit_card', 'paypal']
-- models/marts/orders_since.sql
select *
from {{ ref('stg_orders') }}
where ordered_at >= '{{ var("start_date") }}'dvt run --vars '{"start_date": "2025-01-01"}' # CLI wins over the fileExactly dbt's semantics: the CLI value outranks the file, and var('key', 'fallback') takes a second argument for a default when the key is declared nowhere.
WHAT NEVER GOES HERE: DVT'S OWN SETTINGS
DVT keeps dbt_project.yml byte-pure dbt — it never reads a DVT setting from this file. Project-level DVT settings (federation_direct, suite_port, ai_enabled, telemetry_enabled) live in dvt_project.yml, a small flat file scaffolded right beside this one.
Put one of those keys in dbt_project.yml anyway and DVT refuses by name instead of shrugging:
Configuration Error in dbt_project.yml (./dbt_project.yml)
'suite_port' is a DVT setting, found at: suite_port
dbt_project.yml is byte-pure dbt and DVT never reads its own keys from it.
Move it to dvt_project.yml beside it — one flat key at the top level:
# dvt_project.yml
suite_port: <value>In plain dbt, a stray +key inside the models:tree is silently merged into every model's config and read by nothing — you find out never. DVT names the keys it owns and points you to their real home, so the two files can never half-configure each other.
REFERENCE: THE KEYS AT A GLANCE
| KEY | DEFAULT | WHAT IT DOES |
|---|---|---|
| name | required | The project's name — lowercase letters, digits, underscores. Namespaces the models:/seeds: config trees. |
| version | — | Your own version label for the project. Any string; informational. |
| profile | required to run | Which profiles.yml entry supplies this project's connections and default target. |
| model-paths | ["models"] | Folders scanned for models (SQL and Python) and their .yml property files. |
| seed-paths | ["seeds"] | Folders scanned for seed files: csv, parquet, json, jsonl. |
| snapshot-paths | ["snapshots"] | Folders scanned for snapshot definitions. |
| test-paths | ["tests"] | Folders scanned for singular data tests. |
| analysis-paths | ["analyses"] | Folders for analysis SQL — compiled, never run. |
| macro-paths | ["macros"] | Folders scanned for Jinja macros. |
| target-path | "target" | Where compiled SQL and run artifacts are written. |
| clean-targets | ["target", "dbt_packages"] | Folders removed by dvt clean. |
| models | {} | Folder-level model configs (+materialized, +schema, +tags, …). Accepts DVT's f_table / f_incremental as +materialized values. |
| seeds | {} | Folder- and seed-level seed configs, including +column_types. |
| snapshots | {} | Folder-level snapshot configs, same inheritance as models. |
| vars | {} | Project variables, read with {{ var('...') }}; overridden by --vars on the CLI. |
| flags | {} | dbt behavior flags. The scaffold sets send_anonymous_usage_stats: false — DVT's disclosed telemetry is the one channel. |
| on-run-start / on-run-end | [] | SQL hooks run at the start and end of dvt run / build / seed, per dbt's hook semantics. |
| quoting | engine defaults | Per-part identifier quoting (database / schema / identifier), as in dbt. |
Anything else dbt documents for dbt_project.ymlworks here too, with dbt's semantics — this table is the working set, not a fence. That is the whole design: total compatibility, so your project stays a valid dbt project and the file you already know keeps meaning what it always meant.