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: stringWHERE 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
| FLAG | DEFAULT | WHAT IT DOES |
|---|---|---|
| --select, -s | all seeds | Selection — 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. |
| --exclude | none | Selection — which seeds to leave out (repeatable). Same resolution rules as --select. |
| --full-refresh | off | Drop and recreate seed tables. Default is truncate + reload, which keeps dependent views intact; use full-refresh when the file's shape changed. |
| --column-casing | snake | Normalize 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, -x | off | Stop 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, -t | profile default | Target output to load into — the same output resolution as dvt run --target. |
| --threads | profile threads | Parallel 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 ~/.dbt | Profiles directory — resolved the same way as every other command. |
| --debug | off | Debug logging. |
Exit codes: 0 on a clean run — including an honest Nothing to do — 1 when any seed failed to load, 2 when the project failed to parse (zero seed work).