CertSafari
    DBT Architect· Lessons

    Domain 3 · Lesson 11/39

    Environment variables in dbt: precedence, secrets and built-ins

    Understanding environment variables

    16 min read
    2.56% of exam
    4 sources
    Published 12 Oct 2026
    Docs as of 12 Oct 2026

    What you will be able to do

    • Name environment variable keys that the dbt platform will accept and reference them with env_var()
    • Predict which value a job or Studio IDE session receives under the four-level order of precedence
    • Choose between project, environment, job and personal levels to make a project behave differently in development, staging, production and CI
    • Use DBT_ENV_SECRET_ and DBT_ENV_CUSTOM_ENV_ variables within their restrictions
    • Use built-in variables such as DBT_CLOUD_GIT_BRANCH and DBT_CLOUD_INVOCATION_CONTEXT, and know where each one is available
    • Cast string values to integers and booleans, and set variables for local runs

    Key concept

    Layered environment variables — A dbt environment variable is a named value that project code reads through env_var(). dbt resolves it from the most specific level that sets it: a job or personal override first, then the environment, then the project default, then the default given in code. The same code can therefore behave differently depending on where it runs.

    1.Referencing environment variables with env_var

    An environment variable lets one dbt project behave differently depending on where it runs. The same model code can build into a developer's schema in the Studio IDE, a staging database in a pre-release job and production tables on the nightly schedule. Nothing in the repository changes between those runs. The code asks for a value by name, and dbt supplies that value at run time.

    The env_var Jinja function reads the value. It takes a key and an optional default: {{ env_var('DBT_KEY', 'OPTIONAL_DEFAULT') }}. You can call it anywhere dbt processes Jinja, including profiles.yml, dbt_project.yml, source and schema files, and model .sql files. Inside YAML, put quotes around the whole Jinja string, or the curly brackets will confuse the YAML parser. A default keeps compilation working when the variable isn't available. In this materialization setting, the default is view:

    A model config that falls back to 'view' when DBT_MATERIALIZATION is not setyaml
    +materialized: "{{ env_var('DBT_MATERIALIZATION', 'view') }}"

    The dbt platform enforces strict naming. Every key must start with DBT_, DBT_ENV_SECRET_ or DBT_ENV_CUSTOM_ENV_. Keys are uppercase and case sensitive. The key passed to env_var() must exactly match the variable defined in the dbt UI, so env_var('DBT_Warehouse') will not find a variable saved as DBT_WAREHOUSE. When a self-hosted project moves to the platform, any variable names without the prefix need a find-and-replace across the project.

    Checkpoint 1 of 9· Check yourself

    An admin is adding a variable in the dbt platform that models will read to pick a warehouse. Which key is valid?

    Sources12

    2.The four levels of precedence

    A value can be set at four levels, and dbt uses the most specific one that exists. From lowest to highest precedence, the levels are the default argument in the env_var() call, the project default, the environment value, and a job override or a developer's personal override. A lower level only applies when no level above it sets a value.

    Where each precedence level is set and what overrides it
    LevelWhere it is setOverridden by
    Default argument in env_var()Project codeEvery other level
    Project defaultOrchestration > Environments > Environment variables, Project default columnEnvironment, job or personal override
    EnvironmentSame page, one column per environmentJob override or personal override
    Job overrideThe job's settings, in the environment variable override sectionNothing (highest precedence)
    Personal overrideAccount settings > Credentials > project > Environment variablesNothing (highest precedence, Studio IDE only)

    Project and environment values are set on the same screen: Orchestration > Environments > Environment variables. A value in the Project default column applies wherever the code runs, which makes it the place for a catch-all fallback or a project-wide token or secret. Each environment has its own column to the right. That is where you give staging and production different values.

    A job inherits the values of the deployment environment it runs in. Several jobs often share one environment. When one of them needs a different value, its settings let you override any variable defined at the environment or project level. That setting is covered with the other [optional job settings](lesson:4.6).

    Developers can override values too, in the Studio IDE. Without a personal override, the IDE uses the values set in the project's development environment. A developer's override applies to both the Results and the Compiled SQL tabs.

    Checkpoint 2 of 9· Put it in order

    Put the steps for setting a personal override in the Studio IDE in order

    1. 1.Under Your profile, click Credentials and select the project
    2. 2.Enter the value to use in place of the development environment's value
    3. 3.Scroll to the Environment variables section and click Edit
    4. 4.Click your account name in the left side menu and select Account settings

    A gap in this chain causes a hard failure. Suppose a variable has no project default, nothing sets it in the context where the code is running, and the env_var() call has no default. Compilation then stops with Env var required but not provided. Setting a project default for every variable means dbt always has a value to use in every context.

    Checkpoint 3 of 9· Exam question

    A team defines `DBT_WAREHOUSE` with a project-level default of `SMALL`, overrides it to `MEDIUM` at the Staging environment, and then overrides it again to `LARGE` for one specific job inside that environment. When that job runs, which value does dbt resolve for `DBT_WAREHOUSE`?

    Sources1

    3.Making jobs and connections environment-aware

    A common use of a job override is matching compute to the workload. Say a full-refresh job needs an XL Snowflake warehouse, but the incremental job in the same environment runs fine on a medium one. Instead of naming the warehouse in the connection, set it to {{env_var('DBT_WAREHOUSE')}} and give each job its own DBT_WAREHOUSE value. The same approach works with a Databricks SQL Warehouse.

    A value is fixed for the whole run. Each env_var has a single value for the entire run, so you can't switch warehouses between models in one run. That needs two jobs.

    Connections are where variables and authentication meet. Env vars work with username/password and key pair credentials, including in scheduled jobs. With Snowflake OAuth, they can set database, warehouse and role, but not the account/host field. Extended attributes have their own limit. When a deployment environment uses profiles, any env_var reference in its extended attributes must point to a project-scoped variable. Profiles don't belong to any one environment, so a connection test can't see a value set only in an environment's column. The test fails even though real job runs succeed. Setting the value in the Project default column fixes this.

    Variables are also the recommended way to tell environments apart in code. You can set custom target.name values in development credentials or orchestration settings. Environment variables are the preferred method because dbt's partial parsing handles them better than {{ target }} logic.

    In the Studio IDE, a value changed mid-session may not take effect until the IDE restarts. Click the status message in the bottom right corner and choose Restart IDE. If compiled output still shows old values, delete target/partial_parse.msgpack to force dbt to re-compile the whole project.

    Checkpoint 4 of 9· Check yourself

    A deployment environment that uses profiles has password: '{{ env_var(''DBT_ENV_SECRET_PASSWORD'') }}' in its extended attributes. The variable has a value only in the Production column. Scheduled jobs succeed, but the connection test fails. What fixes the test?

    Sources134

    4.Secret and custom metadata prefixes

    All environment variables are encrypted at rest. The DBT_ENV_SECRET_ prefix adds more protection. A secret's value is removed from logs and error messages and replaced with *****. It is hidden in the UI and in artifacts, and it is decrypted only when a flow needs it, such as a job run. The main use is a git access token for cloning private packages.

    Secrets stay protected because dbt limits where they can be used. They are available only in profiles.yml and packages.yml. Inside those files, dbt inserts the plain string value and nothing more. You can't apply Jinja filters to a secret, including as_number or as_bool, and you can't pass it into other macros. Each configuration value can hold only one secret:

    One secret per configuration value works. Building a value from two secrets does not.yaml
    # works
    host: "{{ env_var('DBT_ENV_SECRET_HOST') }}"
    
    # does not work
    host: "www.{{ env_var('DBT_ENV_SECRET_HOST_DOMAIN') }}.com/{{ env_var('DBT_ENV_SECRET_HOST_PATH') }}"

    A git token stored as a variable should still have limited permissions. Make it read-only, and prefer a PAT from a machine account or service user with limited repo access over a person's own token.

    DBT_ENV_CUSTOM_ENV_ does the opposite of the secret prefix: it publishes values. dbt strips the prefix from each of these variables and adds them under env in the artifact metadata and under info -> extra in structured logs. The dbt_metadata_envs context variable also exposes them as a dictionary. Run with DBT_ENV_CUSTOM_ENV_MY_FAVORITE_COLOR=indigo, and {{ dbt_metadata_envs }} compiles to a dictionary with the key MY_FAVORITE_COLOR.

    Checkpoint 5 of 9· Check yourself

    Which of these uses of a DBT_ENV_SECRET_ variable will fail?

    Checkpoint 6 of 9· Exam question

    A data engineer needs to pass a private repository access token into a dbt Cloud job so dbt can pull a private package, and the token must never appear in run logs, error messages, or the dbt Cloud UI. How should the engineer define this environment variable?

    Sources21

    5.Built-in variables dbt sets for you

    dbt sets some variables itself. You can't change them, and the order of precedence doesn't apply to them. No project default, environment value or job override replaces them. They tell your code which environment, job, run or branch it is running in.

    Built-in variables, their values and where they are available
    VariableWhat it resolves toAvailable
    DBT_CLOUD_GIT_BRANCHThe development Git branch name. Updates when the branch changes.Studio IDE only. Not in the dbt CLI.
    DBT_ENVAlways 'prod'Deployment runs only
    DBT_CLOUD_ENVIRONMENT_TYPEdev, staging or prod. Empty for General deployment environments.Set automatically
    DBT_CLOUD_INVOCATION_CONTEXTdev, staging, prod or ciSet automatically
    DBT_CLOUD_RUN_REASON_CATEGORYscheduled, github_pull_request, gitlab_merge_request, azure_pull_request or otherSet automatically
    DBT_CLOUD_PR_ID, DBT_CLOUD_GIT_SHAThe pull request ID and the commit SHA being builtGitHub, GitLab and Azure DevOps PR builds triggered by webhook

    DBT_CLOUD_GIT_BRANCH is a Studio IDE variable. It follows the developer's branch as it changes, with no IDE restart, so you can use the branch name as a prefix for a development schema. It is not available in the dbt CLI. Code that calls it without a default fails there.

    DBT_CLOUD_ENVIRONMENT_TYPE is empty in a General deployment environment, so always give it a default:

    Reading the environment type safely when a General environment leaves it emptyjinja
    {{ env_var('DBT_CLOUD_ENVIRONMENT_TYPE', '') }}

    DBT_CLOUD_INVOCATION_CONTEXT is the variable that tells CI apart from production, because one of its values is ci. Use it in the generate_schema_name() macro to make CI runs use only the default schema, with the dbt_cloud_pr prefix. This holds even when CI jobs run in the same environment as production jobs. The macro itself belongs with [default schemas for environments](lesson:3.6).

    Checkpoint 7 of 9· Match them up

    Match each built-in variable to what it tells your code

    Tap a term, then the definition that fits it.

    Checkpoint 8 of 9· Exam question

    A model calls `{{ env_var('DBT_REGION') }}` with no second argument, and `DBT_REGION` has not been set at the project, environment, or job level. What happens when the job runs?

    Sources1

    6.String values and local runs

    Every environment variable value is a string. That doesn't matter for a schema name, but it breaks settings such as threads or persist_docs that expect a number or a boolean. An unconverted '1' fails with '1' is not of type 'integer'. Convert the value with a filter: int or as_number for integers, as_bool for booleans. Write boolean defaults as capitalized True or False. Lowercase true is treated as a string and can give unexpected results.

    Checkpoint 9 of 9· Fill the gap

    Which filter makes this threads setting an integer?

    "{{ env_var('DBT_THREADS') |  ?  }}"

    Outside the platform, variables come from your own machine. Local dbt and the dbt VS Code extension read them from your shell profile (~/.zshrc or ~/.bashrc) or from a .env file. If both set the same variable, the shell value wins over .env. dbt loads .env only from the directory you run commands from. It ignores --project-dir, so keep the file at the project root, run dbt from there and add .env to .gitignore. The extension's menu actions don't pick up variables exported in a terminal. Set those in the dbt.environmentVariables user setting or in .env. When you copy values over from the platform's Environment variables tab, masked secrets stay hidden, so get those values from an admin.

    Sources2

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.A DBT_ENV_SECRET_ variable can be used in model SQL or dbt_project.yml, and dbt will just mask it in the logs.Why is that wrong?

      Secret variables are available only in profiles.yml and packages.yml. Everywhere else they are rejected, so they can't leak into the warehouse or artifacts.

      Covered in Secret and custom metadata prefixes

    2. 2.DBT_CLOUD_GIT_BRANCH can prefix development schemas for developers using either the Studio IDE or the dbt CLI.Why is that wrong?

      dbt sets DBT_CLOUD_GIT_BRANCH automatically in the Studio IDE only. It is not available in the dbt CLI.

      Covered in Built-in variables dbt sets for you

    3. 3.If jobs in a deployment environment succeed, an env_var in its extended attributes is configured correctly, so the connection test should pass too.Why is that wrong?

      Profiles don't belong to any one environment, so a connection test only sees project-scoped values. A variable set only at the environment level makes the test fail while jobs still run.

      Covered in Making jobs and connections environment-aware

    4. 4.Writing env_var('DBT_FLAG', false) | as_bool with a lowercase default gives a proper boolean default.Why is that wrong?

      Boolean defaults must be capitalized True or False. Lowercase is treated as a string.

      Covered in String values and local runs

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “Environment variables in dbt must be prefixed with either DBT_, DBT_ENV_SECRET_, or DBT_ENV_CUSTOM_ENV_.”
      ↩︎ Referencing environment variables with env_var
      “By default, dbt uses environment variable values set in the project's development environment.”
      ↩︎ The four levels of precedence
      “The job level (job override) or in the Studio IDE for an individual dev (personal override). (highest precedence)”
      ↩︎ The four levels of precedence
      “dbt will throw a compilation error”
      ↩︎ The four levels of precedence
      “each env_var can only have a single set value for the entire duration of the run.”
      ↩︎ Making jobs and connections environment-aware
      “Secret keys are never written to logs or error messages”
      ↩︎ Secret and custom metadata prefixes
      “Variables are set automatically and cannot be changed.”
      ↩︎ Built-in variables dbt sets for you
      “The value will be empty for General deployment environments”
      ↩︎ Built-in variables dbt sets for you
      “Environment variables can be used to customize the behavior of a dbt project depending on where the project is running.”
      ↩︎ Key concept
      “Currently not available in the dbt CLI.”
      ↩︎ Exam trap 2
      “Values set at the environment level take priority over the project-level default value.”
      ↩︎ Prediction
      “Under the Your profile section, click Credentials and then select your project.”
      ↩︎ Checkpoint
      “This key is reserved for the dbt application and will always resolve to 'prod'.”
      ↩︎ Checkpoint
    2. 2.
      “Essentially, env_var is available anywhere dbt processes Jinja code.”
      ↩︎ Referencing environment variables with env_var
      “The primary use case of secret env vars is git access tokens for private packages.”
      ↩︎ Secret and custom metadata prefixes
      “Any env var named with the prefix DBT_ENV_CUSTOM_ENV_ will be included in two places, with its prefix-stripped name as the key:”
      ↩︎ Secret and custom metadata prefixes
      “Environment variables are always strings.”
      ↩︎ String values and local runs
      “Shell environment variables take precedence over values in .env and will not be overridden by the file.”
      ↩︎ String values and local runs
      “Disallowed everywhere else, including dbt_project.yml and model SQL”
      ↩︎ Exam trap 1
      “Using lowercase true or false will be treated as a string and can result in unexpected results.”
      ↩︎ Exam trap 4
      “Disallowed everywhere else, including dbt_project.yml and model SQL”
      ↩︎ Prediction
      “You cannot modify secrets using Jinja filters, including type-casting filters such as as_number or as_bool”
      ↩︎ Checkpoint
    3. 3.
      “any env_var references in Extended Attributes must use project-scoped environment variables”
      ↩︎ Making jobs and connections environment-aware
      “connection tests will fail if the referenced variable is only set at the environment level”
      ↩︎ Exam trap 3
      “connection tests will fail if the referenced variable is only set at the environment level”
      ↩︎ Checkpoint
    4. 4.
      “we recommend using environment variables as the preferred method”
      ↩︎ Making jobs and connections environment-aware

    Ready to test yourself?

    Practise the 9 questions on this subdomain.

    Spotted a mistake, or was something unclear? Tell us.