dbt Cheatsheet
Core commands, flags, and Jinja/YAML syntax youโll actually reach for mid-task. For the full explanations, see the dbt guides.
Commands
| Command | Description |
|---|---|
dbt run | Compiles and executes every modelโs SQL against the warehouse, in DAG order. Does not run tests. |
dbt test | Runs generic + singular tests. Non-zero exit code on any failure โ usable directly as a CI gate. |
dbt build | Interleaves seeds, models, snapshots, and their tests in one dependency-ordered run. Downstream models are SKIPped after a failed test. |
dbt seed | Loads CSVs from seeds/ as warehouse tables. Insert-only by default โ doesnโt reconcile deletes/edits. |
dbt snapshot | Executes {% snapshot %} blocks, comparing current source state vs. the last captured version. |
dbt debug | Validates dbt_project.yml, profiles.yml, and a live connection. Run this first in new CI setups. |
dbt run-operation <macro> | Invokes a macro directly, outside the model-build lifecycle. |
dbt clean | Deletes local dirs in clean-targets (target/, dbt_packages/). Touches nothing in the warehouse. |
dbt deps | Downloads packages declared in packages.yml into dbt_packages/. |
dbt docs generate | Produces manifest.json and catalog.json in target/. |
dbt docs serve | Hosts the generated docs site locally (default localhost:8080). |
dbt compile | Resolves Jinja/refs without executing โ writes to target/compiled/. |
dbt parse | Structural manifest only, no live warehouse connection needed. |
dbt source freshness | Checks raw source staleness against loaded_at_field; writes target/sources.json. |
dbt ls | Lists/selects nodes โ models, tests by tag, etc. |
dbt init <project> | Scaffolds a new project. |
Flags
| Flag | Applies to | Purpose |
|---|---|---|
--select | run, test, build, seed, snapshot, ls | Scopes to a model/graph selection |
--exclude | build, run | Excludes matching nodes |
--full-refresh | run, seed | Forces incremental/seed rebuild from scratch |
--target | run, debug | Chooses the warehouse/environment connection |
--threads | run | Controls model concurrency |
--store-failures | test | Materializes failing rows into a queryable table |
--indirect-selection=cautious|eager | test | Controls whether multi-model tests run on partial selections |
--fail-fast | build | Stops the entire run at the first failure |
--vars '{"key": "value"}' | run, etc. | Overrides project vars for a single invocation |
--args '{"k": "v"}' | run-operation | Passes macro arguments as JSON |
--config-dir | debug | Shows where profiles.yml is being read from |
--no-connection | debug | Validates config files without a live connection test |
--profiles-dir | any | Overrides the location of profiles.yml |
--debug | any | Prints compiled SQL + connection details |
Selection syntax
| Syntax | Meaning |
|---|---|
model_name | Just that model |
model_name+ | Model + everything downstream |
+model_name | Model + everything upstream |
+model_name+ | Model + both directions |
tag:x | All models tagged x |
source:* | All sources |
test_type:singular / test_type:generic | Filter by test type |
resource_type:snapshot | Filter by resource type |
staging.* | All models under a folder path |
dbt run --select stg_orders+dbt test --select unique_stg_orders_order_id --store-failuresdbt build --select tag:finance --fail-fastSyntax
Model config
{{ config(materialized='incremental', unique_key='order_id') }}models: my_project: staging: +materialized: view marts: +materialized: tableIncremental pattern:
{{ config(materialized='incremental', unique_key='event_id') }}select event_id, user_id, event_type, occurred_atfrom {{ ref('stg_events') }}{% if is_incremental() %} where occurred_at > (select max(occurred_at) from {{ this }}){% endif %}source() and ref()
select id as order_id from {{ source('raw', 'orders') }} where status != 'test'select * from {{ ref('stg_customers') }}select * from {{ ref('finance_project', 'monthly_revenue') }} -- cross-project (dbt Mesh)version: 2sources: - name: raw database: analytics_raw schema: raw_app_data loaded_at_field: _fivetran_synced freshness: warn_after: {count: 12, period: hour} error_after: {count: 24, period: hour} tables: - name: orders - name: customersref() resolves at compile time, not runtime โ a typo fails immediately with a clear error instead of a confusing runtime SQL error. Only staging models should call source() directly โ everything downstream should go through ref().
Macros
-- macros/cents_to_dollars.sql{% macro cents_to_dollars(column_name) %} ({{ column_name }} / 100.0){% endmacro %}Called as {{ cents_to_dollars('amount_in_cents') }}. Macro returning a config dict:
{% macro standard_incremental_config(unique_key) %} {{ return({ 'materialized': 'incremental', 'unique_key': unique_key, 'incremental_strategy': 'merge' }) }}{% endmacro %}Tests
Generic tests (built-in):
models: - name: stg_orders columns: - name: order_id tests: [unique, not_null] - name: status tests: - accepted_values: values: ['pending', 'shipped', 'delivered', 'cancelled', 'returned'] - name: customer_id tests: - relationships: to: ref('stg_customers') field: customer_idSingular test (fails if the query returns any rows):
-- tests/assert_no_negative_completed_order_totals.sqlselect order_id, total_amount from {{ ref('stg_orders') }}where status = 'completed' and total_amount < 0Custom generic test macro:
-- macros/test_not_negative.sql{% test not_negative(model, column_name) %}select * from {{ model }} where {{ column_name }} < 0{% endtest %}Snapshots
{% snapshot customers_snapshot %}{{ config( target_schema='snapshots', unique_key='customer_id', strategy='timestamp', updated_at='updated_at',) }}select * from {{ source('raw', 'customers') }}{% endsnapshot %}check strategy variant: strategy='check', check_cols=['region', 'email', 'plan_tier']. Managed columns added automatically: dbt_scd_id, dbt_valid_from, dbt_valid_to.
var() and Jinja
vars: start_date: '2024-01-01'where order_date >= '{{ var("start_date") }}'where event_date >= '{{ var("lookback_start_date", "2020-01-01") }}' -- inline fallbackdbt run --select customer_orders --vars '{"start_date": "2025-06-01"}'Loop + conditional:
{% set status_list = ['completed', 'shipped', 'delivered'] %}select * from {{ ref('stg_orders') }}where status in ( {% for status in status_list %} '{{ status }}'{% if not loop.last %},{% endif %} {% endfor %})Use var() for values that could reasonably be committed to git; use env_var() for secrets or environment-specific infra details.
Project config
name: my_projectversion: 1.0profile: my_profilemodel-paths: ["models"]models: my_project: staging: { materialized: view } marts: { materialized: table }# ~/.dbt/profiles.yml (never committed)my_project: target: dev outputs: dev: type: postgres host: localhost user: analyst password: "{{ env_var('DBT_PASSWORD') }}" dbname: analytics schema: dbt_dev threads: 4packages: - package: dbt-labs/dbt_utils version: [">=1.1.0", "<2.0.0"]Gotchas
dbt runnever validates data โ zero errors means the SQL executed without a database error, not that the resulting data is correct. Usedbt test/dbt buildfor that.- Only
dbt buildprevents downstream models from building on bad upstream data within the same invocation โ plainrun+testdoesnโt stop the pipeline mid-flight. - Seeds are insert-only by default โ stale/duplicate rows are the most common seed bug. Same applies to forgetting
--full-refreshafter changing incremental logic. - Snapshot history is irreversible โ if a row changes twice between two scheduled snapshot runs, only the second change survives; the first is lost with no way to recover it. Never run snapshots against CI/ephemeral databases.
dbt cleanonly touches local filesystem artifacts โ it does not reset warehouse state or fix a broken model.- Always gitignore
dbt_packages/โ the same way you wouldnโt commitnode_modules.