Data Engineering  /  dbt

๐Ÿ”„ dbt โ€” Data Build Tool 60 guides ยท updated 2026

Analytics engineering with SQL โ€” models, tests, sources, and Jinja macros that turn raw warehouse tables into trustworthy, documented data products.

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

Terminal window
dbt seed

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

Insert-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.

Terminal window
# If you edited or removed rows in the CSV, this alone may not reflect it
dbt seed
# This drops and recreates the table from the CSV's current contents
dbt seed --full-refresh

In 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.

Terminal window
dbt seed --select country_region_mapping
dbt seed --select country_region_mapping --full-refresh

Overriding 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.

dbt_project.yml
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_data

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

Terminal window
dbt seed
dbt run
dbt test

Or 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.

Terminal window
# Correct order for a fresh environment with no existing seed tables
dbt seed
dbt run

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

Almost 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/BehaviorEffect
dbt seedLoads CSVs, inserting new rows into existing tables
dbt seed --full-refreshDrops and recreates seed tables from current CSV contents
+column_typesOverrides type inference for specific columns
--selectLoads 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.