dbt Targets and Environments
A target in dbt is a named database configuration defined in profiles.yml. Targets let you run the same dbt project against different databases or schemas — typically a development environment for testing and a production environment for live data. Switching environments changes where dbt writes output without changing any SQL code.
What a Target Is
profiles.yml structure:
my_project:
target: dev ← default target when none specified
outputs:
dev: ← target named "dev"
type: snowflake
schema: dbt_dev
...
prod: ← target named "prod"
type: snowflake
schema: dbt_prod
...
ci: ← target named "ci"
type: snowflake
schema: "dbt_ci_{{ env_var('GITHUB_RUN_ID') }}"
...
Switching Targets
# Use the default target (dev in the example above) dbt run # Use a specific target dbt run --target prod dbt run --target ci dbt test --target prod dbt build --target ci
How Targets Separate Environments
Same SQL:
select * from {{ ref('stg_orders') }}
Target dev:
→ reads from dbt_dev.stg_orders
→ writes to dbt_dev.fct_orders
Target prod:
→ reads from dbt_prod.stg_orders
→ writes to dbt_prod.fct_orders
Your SQL never changes. The target determines the schema prefix applied to all model names.
target Object in Jinja
The target object is available in all Jinja contexts and exposes the current target's properties:
{{ target.name }} → 'dev', 'prod', 'ci'
{{ target.schema }} → 'dbt_dev', 'dbt_prod'
{{ target.database }} → 'ANALYTICS'
{{ target.type }} → 'snowflake', 'bigquery', 'postgres'
{{ target.threads }} → 4, 8
{{ target.profile_name }} → 'my_project'
Environment-Specific SQL Logic
-- Limit rows in dev to speed up development runs
select *
from {{ source('analytics', 'raw_events') }}
{% if target.name != 'prod' %}
where event_date >= dateadd(day, -7, current_date)
-- Dev only loads last 7 days (not 3 years of history)
{% endif %}
-- Use a mock table in dev, real table in prod
{% if target.name == 'prod' %}
from {{ ref('fct_orders') }}
{% else %}
from {{ ref('fct_orders_dev_sample') }} ← smaller, faster sample
{% endif %}
Custom Schema Per Target
Each developer can work in their own personal schema by customizing the schema field:
my_project:
target: dev
outputs:
dev_alice:
type: postgres
schema: dbt_alice
...
dev_bob:
type: postgres
schema: dbt_bob
...
prod:
type: postgres
schema: analytics
...
Three-Environment Setup (Standard Pattern)
Environment Target Name Schema Purpose ----------- ----------- ------ ------- Development dev dbt_[username] Individual developer testing CI ci dbt_ci_[run_id] Automated tests on pull requests Production prod analytics Live data for dashboards
# Local development dbt build --target dev # CI pipeline (GitHub Actions, etc.) dbt build --target ci # Production deployment dbt build --target prod
generate_schema_name Macro
By default, dbt appends the custom schema suffix to your target schema. A model with +schema: finance in a dev environment with schema dbt_dev writes to dbt_dev_finance. Override this with the generate_schema_name macro in production to write to just finance:
-- macros/generate_schema_name.sql
{% macro generate_schema_name(custom_schema_name, node) -%}
{%- set default_schema = target.schema -%}
{%- if custom_schema_name is none -%}
{{ default_schema }}
{%- elif target.name == 'prod' -%}
{{ custom_schema_name | trim }} ← production uses clean schema name
{%- else -%}
{{ default_schema }}_{{ custom_schema_name | trim }} ← dev prefixes
{%- endif -%}
{%- endmacro %}
Result in dev: dbt_dev_finance Result in prod: finance
Verifying Your Active Target
dbt debug Output includes: target: dev profile: my_project output: dev schema: dbt_dev connection test: OK
