DVT SEED

dvt seedloads the data files under your project's seed paths into the target as tables — the command you know from dbt, with the same semantics in the same place in the DAG. What changes is underneath: the bytes move through a COPY-class bulk loader in parallel, and every seed that lands is recorded in DVT's materialization ledger.

This page is the command reference — invocation, output, flags. For what seeds are — when to use them, the four file formats, configuration — start with the Seeds guide.

THE MINIMAL INVOCATION

No flags needed. Seed discovers every loadable file under seeds/ (recursively), and each one lands in the target schema under its filename stem:

$ dvt seed
14:31:02  Concurrency: 4 threads (target='pg_warehouse')

14:31:02  1 of 3 START seed file analytics.country_codes ................................................. [RUN]
14:31:02  2 of 3 START seed file analytics.plan_features ................................................. [RUN]
14:31:02  3 of 3 START seed file analytics.historical_fx_rates ........................................... [RUN]
14:31:03  2 of 3 OK loaded seed file analytics.plan_features .............................. [INSERT 18 in 0.79s]
14:31:03  1 of 3 OK loaded seed file analytics.country_codes ............................. [INSERT 249 in 0.91s]
14:31:11  3 of 3 OK loaded seed file analytics.historical_fx_rates ................... [INSERT 1046572 in 8.63s]

14:31:11  Finished running 3 seeds in 0 hours 0 minutes and 8.79 seconds (8.79s).
14:31:11  Completed successfully

14:31:11  Done. PASS=3 WARN=0 ERROR=0 SKIP=0 TOTAL=3

Read the tallies: INSERT 249 in 0.91s is the row count each load moved, and the last line is the same PASS/WARN/ERROR/SKIP summary every DVT graph command ends on. Notice all three seeds startat once — loads run in parallel up to your profile's threads — and notice the third line: a million-row seed in under nine seconds is the bulk-loading lane doing its job.

IN PLAIN DBT

dbt seedparses the file in Python and pushes rows through the adapter's INSERT path — fine for a hundred rows of country codes, painful for anything real. DVT hands the file to Sling for COPY-class bulk ingestion instead: 10-100x faster on large files, same semantics, same tables at the end. The classic advice to keep seeds tiny was partly about the loader; that part is gone.

If there is nothing under the seed paths, seed says so and exits 0 — Nothing to do: no seed files found — an empty project is not an error.

SELECTION — --SELECT AND --EXCLUDE

Bare seed names select by name, exactly as you'd expect:

$ dvt seed --select country_codes

Anything richer — graph operators, wildcards, tag:, path:, config. methods — is selection syntax, and DVT resolves it through the engine's own selector, so the answer is exactly what a native build would say. That means graph operators genuinely walk the graph: select a model with + and you load precisely the seeds it depends on, nothing else.

$ dvt seed --select +customer_ltv     # the seeds that model actually needs
$ dvt seed --select tag:reference     # every seed tagged reference
$ dvt seed --exclude historical_fx_rates

Both flags repeat, and a quoted, space-separated list works too. Two honest edges: a selection whose bare names match no seed at all warns Seeds not found: … before doing nothing, but a token that names a model is a perfectly good selection with an empty seed slice — no warning, no work, exit 0.

TRUNCATE BY DEFAULT, --FULL-REFRESH TO REBUILD

A plain dvt seedtruncates each table and reloads it — dbt's own semantics, and the reason dependent views survive a reload untouched. --full-refresh drops and recreates the table instead. Since a seed is its file, both end with the table mirroring the file exactly; reach for --full-refresh when the file's shapechanged — a column added, renamed or retyped — and the existing table can't absorb it.

$ dvt seed --select plan_features --full-refresh

--THREADS, AND TARGETS THAT CAN'T SHARE

Loads run in parallel — --threadsoverrides the profile's setting for one run. But a single-writer file database (a DuckDB or SQLite target) takes one writer at a time: running eight loaders against it isn't a slow path, it's seven lock failures and a half-seeded database. Seed serializes instead — and says so, because DVT never silently does less than the profile asked for:

$ dvt seed
14:38:41  Concurrency: 1 threads (target='dev_duckdb')
14:38:41  Serialized: 'dev_duckdb' is a duckdb file database — one writer at a time, so the profile's threads:8 would collide on its lock.

--FAIL-FAST — STOP AT THE FIRST FAILURE

By default a failed seed doesn't stop the others — the run finishes and the summary names each failure. With --fail-fast (or -x), whatever is already loading finishes, nothing new starts, and the seeds that never ran are counted as skipped, not swept under the rug:

14:42:19  2 of 6 ERROR loading seed file analytics.plan_features .............................. [ERROR in 0.31s]
14:42:19  Aborting --fail-fast: a seed failed ......................................................... [SKIP 4]

14:42:19  Finished running 6 seeds in 0 hours 0 minutes and 1.27 seconds (1.27s).
14:42:19  Completed with 1 error(s):
14:42:19    Failure in seed plan_features
14:42:19      permission denied for schema analytics

14:42:19  Done. PASS=1 WARN=0 ERROR=1 SKIP=4 TOTAL=6

Either way the exit code is 1 when anything failed, so CI catches it — and inside dvt build, models depending on a failed seed are marked SKIPPED rather than dying downstream on an error naming the wrong node.

--COLUMN-CASING — HEADERS YOU CAN QUERY EVERYWHERE

Seed columns are normalized to snake_case on load by default — SKU Code becomes sku_code, deterministically, on every engine. It's the one casing every engine agrees on; spaced and mixed-case headers break some engines' insert paths outright. Pass --column-casing source to keep headers exactly as written, or upper/lower to force a fold. (On an Oracle target, columns land UPPERCASE regardless — its native fold, so unquoted SQL resolves them.)

Column types work the dbt way: +column_types in dbt_project.yml is honored per seed, for the engines whose strict casting rejects an inferred type:

# dbt_project.yml
seeds:
  my_project:
    historical_fx_rates:
      +column_types:
        ordering_date: string

WHERE SEEDS LAND, AND WHAT REMEMBERS THEM

On a fresh target, seed creates the schema if it doesn't exist — a brand-new project's first dvt build never dies on a missing schema. On a bucket target (S3, GCS, Azure), a seed lands as a Parquet objectunder the connection's base path — buckets hold files, not tables.

And every landed seed is written into the materialization ledger at load time — name, connection, schema, relation, row count. That entry is why dvt generate-sourcesnever rediscovers your own seed table as a "source", and why dvt retract can tear a seed down from whichever engine or bucket it actually landed on.

BEFORE ANYTHING LOADS

Seed is a graph-bearing command: it begins from a passing dvt parse, and a broken project is refused with the parse error itself — exit 2, zero seed work, never a half-seeded target. Discovery holds the same line for file formats: a seed is one of DVT's four formats (CSV, Parquet, JSON, JSONL), and a file carrying any other data extension is refused by name, before a single seed loads — never skipped in silence. The Seeds guide covers the formats in full.

REFERENCE — EVERY FLAG

FLAGDEFAULTWHAT IT DOES
--select, -sall seedsSelection — seed names or selection syntax (repeatable). Bare names match directly; graph operators, wildcards and tag:/path:/config. tokens resolve through the engine's own selector, so +model loads exactly a model's seed ancestors.
--excludenoneSelection — which seeds to leave out (repeatable). Same resolution rules as --select.
--full-refreshoffDrop and recreate seed tables. Default is truncate + reload, which keeps dependent views intact; use full-refresh when the file's shape changed.
--column-casingsnakeNormalize column names on load: source, snake, upper or lower. snake is applied by default — the one casing every engine agrees on. Oracle targets land UPPERCASE regardless (its native fold).
--fail-fast, -xoffStop at the first failed seed instead of loading the rest. In-flight loads finish; queued seeds are counted as SKIP in the final tally.
--target, -tprofile defaultTarget output to load into — the same output resolution as dvt run --target.
--threadsprofile threadsParallel loads for this run. Single-writer file targets (DuckDB, SQLite) serialize to one loader, with a printed Serialized: line saying why.
--project-dir.Project directory.
--profiles-dir$DBT_PROFILES_DIR, then ~/.dbtProfiles directory — resolved the same way as every other command.
--debugoffDebug logging.

Exit codes: 0 on a clean run — including an honest Nothing to do1 when any seed failed to load, 2 when the project failed to parse (zero seed work).