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: false

Read 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 build

Precedence 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 downstream

This 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 file

Exactly 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

KEYDEFAULTWHAT IT DOES
namerequiredThe project's name — lowercase letters, digits, underscores. Namespaces the models:/seeds: config trees.
versionYour own version label for the project. Any string; informational.
profilerequired to runWhich 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.
quotingengine defaultsPer-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.