dbt seed Command: Loading CSVs Into Your Warehouse Correctly
dbt seed is a small command with a specific job โ reading the CSV files declared in dbt Seeds and loading them into your warehouse as real tables. Itโs simple in principle, but the details around full refresh behavior, column types, and selection are worth understanding precisely, since seed data tends to be exactly the kind of small, hand-edited reference data where a subtle loading mistake goes unnoticed for a long time.
The Basic Command
dbt seedThis scans the seeds/ directory (or wherever seed-paths in dbt_project.yml points), and for every CSV found, creates or updates a corresponding warehouse table with the same name as the file.
seeds/ country_region_mapping.csv โ creates table: country_region_mapping excluded_test_accounts.csv โ creates table: excluded_test_accountsInsert-Only vs. Full Refresh Behavior
This is the detail that trips people up most often. A plain dbt seed run, on an already-existing seed table, only appends rows โ it does not automatically reconcile deletions or reflect edited/removed rows from the CSV.
# If you edited or removed rows in the CSV, this alone may not reflect itdbt seed
# This drops and recreates the table from the CSV's current contentsdbt seed --full-refreshIn practice, --full-refresh is what you want after almost any edit to a seed file, since seeds represent the current state of a reference dataset, not an append-only log of changes. Forgetting this flag is the single most common seed-related bug โ stale or duplicate rows lingering in the warehouse table long after the CSV was corrected.
Selecting Specific Seeds
On a project with many seed files, loading all of them on every run is unnecessary if you only changed one.
dbt seed --select country_region_mappingdbt seed --select country_region_mapping --full-refreshOverriding Column Types
dbt infers column types from CSV contents by default, which is usually right but occasionally wrong โ most commonly for numeric-looking string codes.
seeds: my_project: country_region_mapping: +column_types: country_code: varchar(2) postal_prefix: varchar(5)Without this override, a postal prefix like "007" risks silently becoming the integer 7 on load โ a bug thatโs easy to miss until a join against it stops matching correctly.
Configuring Where Seeds Land
Like models, seeds can be configured to build into a specific schema, separate from your regular model output โ useful for keeping hand-maintained reference tables visibly distinct from generated model output.
seeds: my_project: +schema: reference_dataRunning Seeds Before Models That Depend on Them
Because seeds are referenced with ref() just like models, dbtโs DAG correctly sequences them โ but only if theyโre actually built. A common production sequencing pattern:
dbt seeddbt rundbt testOr more robustly, using dbt build, covered in dbt build, which interleaves seeds, models, snapshots, and tests in correct dependency order automatically without needing to sequence separate commands manually.
Combining Seed Loading With the Rest of a Build
On projects where seeds feed directly into models that run immediately afterward, sequencing matters โ a model referencing a seed via ref() needs that seed to already exist in the warehouse before it can build successfully on a brand-new environment.
# Correct order for a fresh environment with no existing seed tablesdbt seeddbt rundbt build, covered in dbt build, handles this sequencing automatically since it respects the full DAG including seed dependencies โ but if youโre running commands separately rather than using dbt build, remembering this order matters specifically the first time a new environment is set up, since dbt run alone will fail with a missing-relation error if the seed table doesnโt exist yet.
Common Failure Modes
Database Error in seed country_region_mapping (seeds/country_region_mapping.csv) Could not parse CSV fileAlmost always a malformed CSV โ an unescaped comma inside a field, inconsistent column counts across rows, or an encoding issue. Opening the file in a plain text editor (not a spreadsheet tool, which can silently โfixโ formatting) usually reveals the exact line.
Database Error in seed country_region_mapping invalid input syntax for type integer: "007"A type inference mismatch โ the fix is an explicit +column_types override forcing the column to a string type instead of letting dbt infer a numeric one.
Seed File Size Limits in Practice
Thereโs no hard-coded row limit, but dbt seed isnโt optimized for large files the way a proper ELT loading tool is โ CSVs in the thousands of rows will load noticeably slower than the same volume through a dedicated loader, and CSVs beyond that size are usually a sign the data belongs in a real source pipeline instead of a seed, as discussed in dbt Seeds.
Common Mistakes
Forgetting --full-refresh after editing seed contents. This is by far the most common seed-related surprise โ edits and deletions in the CSV donโt automatically reconcile without it.
Not reviewing seed CSV diffs in pull requests. Since seeds are hand-edited data, not code, itโs easy for a reviewer to skim past a CSV diff without actually checking whether the new row is correct โ worth explicitly calling out in PR review norms.
Using seeds for data too large or too frequently changing for the tool. If a seed file needs weekly edits or has grown into the thousands of rows, itโs a signal to migrate that data into a proper source instead.
Summary
| Flag/Behavior | Effect |
|---|---|
dbt seed | Loads CSVs, inserting new rows into existing tables |
dbt seed --full-refresh | Drops and recreates seed tables from current CSV contents |
+column_types | Overrides type inference for specific columns |
--select | Loads only specific seed files |
dbt seed is simple, but its insert-only default behavior is the one detail worth internalizing early โ reach for --full-refresh as the default expectation any time youโve actually edited a seed fileโs contents.