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:
+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?
Platform keys must start with DBT_ (or one of the longer DBT_ENV_ prefixes) and are uppercase. A DBT suffix doesn't count, and neither does a DBT_ that comes after other text.
“Environment variables in dbt must be prefixed with either DBT_, DBT_ENV_SECRET_, or DBT_ENV_CUSTOM_ENV_.”Source: docs.getdbt.com
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.
| Level | Where it is set | Overridden by |
|---|---|---|
| Default argument in env_var() | Project code | Every other level |
| Project default | Orchestration > Environments > Environment variables, Project default column | Environment, job or personal override |
| Environment | Same page, one column per environment | Job override or personal override |
| Job override | The job's settings, in the environment variable override section | Nothing (highest precedence) |
| Personal override | Account settings > Credentials > project > Environment variables | Nothing (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.Under Your profile, click Credentials and select the project
- 2.Enter the value to use in place of the development environment's value
- 3.Scroll to the Environment variables section and click Edit
- 4.Click your account name in the left side menu and select Account settings
Personal overrides are stored with your credentials for a project, not on the Environments page. You find them through Account settings, then Credentials, then the project's Environment variables section.
“Under the Your profile section, click Credentials and then select your project.”Source: docs.getdbt.com
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`?
Correct answer: A — `LARGE`, because the job-level override takes precedence over both the environment-level and project-level values for that single run.
- A. Job-level and personal overrides sit at the top of the precedence chain, so a value set specifically on this job wins over anything configured at the environment or project scope for that run.
- B. This reverses the actual precedence: the environment-level value is overridden by a job-level entry rather than applied last, so a more specific job setting always wins over it.
- C. The project-level default is the lowest-priority scope and exists mainly as a fallback; it is overridden by both environment-level and job-level values whenever those are defined.
- D. Defining the same variable at multiple scopes is the expected, supported pattern in dbt Cloud, not an error; the platform resolves a single value using its precedence rules instead of rejecting the configuration.
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?
Profiles don't belong to any one environment, so a connection test can only see project-scoped values. Jobs work because they run in a real environment.
“connection tests will fail if the referenced variable is only set at the environment level”Source: docs.getdbt.com
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:
# 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?
dbt inserts a secret as a plain string. Filters such as as_number can't change it, even in profiles.yml.
“You cannot modify secrets using Jinja filters, including type-casting filters such as as_number or as_bool”Source: docs.getdbt.com
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?
Correct answer: A — Name it with the `DBT_ENV_SECRET_` prefix, for example `DBT_ENV_SECRET_GIT_TOKEN`, so dbt Cloud encrypts the value at rest and scrubs it from logs and artifacts.
- A. The `DBT_ENV_SECRET_` prefix is the mechanism dbt Cloud uses to recognize a variable as sensitive, triggering encryption at rest and automatic scrubbing of its resolved value from logs, the UI, and artifacts.
- B. A plain `DBT_` prefix marks a standard variable, not a secret; its resolved value is stored and displayed like any ordinary setting and is not scrubbed from logs the way a secret-prefixed value is.
- C. The custom-environment prefix identifies a different category of variable and does not trigger the encryption or log-scrubbing behavior that only the secret prefix provides.
- D. A default argument written into Jinja code is stored in the project's source files and would appear in compiled output and version control, which is the opposite of keeping a credential hidden.
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.
| Variable | What it resolves to | Available |
|---|---|---|
| DBT_CLOUD_GIT_BRANCH | The development Git branch name. Updates when the branch changes. | Studio IDE only. Not in the dbt CLI. |
| DBT_ENV | Always 'prod' | Deployment runs only |
| DBT_CLOUD_ENVIRONMENT_TYPE | dev, staging or prod. Empty for General deployment environments. | Set automatically |
| DBT_CLOUD_INVOCATION_CONTEXT | dev, staging, prod or ci | Set automatically |
| DBT_CLOUD_RUN_REASON_CATEGORY | scheduled, github_pull_request, gitlab_merge_request, azure_pull_request or other | Set automatically |
| DBT_CLOUD_PR_ID, DBT_CLOUD_GIT_SHA | The pull request ID and the commit SHA being built | GitHub, 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:
{{ 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.
DBT_ENV can't tell runs apart because it is always 'prod'. Use DBT_CLOUD_INVOCATION_CONTEXT to tell CI from production, and DBT_CLOUD_GIT_BRANCH for development work in the IDE.
“This key is reserved for the dbt application and will always resolve to 'prod'.”Source: docs.getdbt.com
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?
Correct answer: A — The run fails at compile time with an error stating that the environment variable is required but was not provided anywhere in scope.
- A. Calling `env_var()` without a default argument makes the variable required; if no value resolves at any scope, dbt Cloud fails compilation with an explicit error rather than guessing a value.
- B. dbt does not silently substitute an empty string for a required variable; leaving the second argument out is precisely what makes the missing value a hard failure instead of a soft fallback.
- C. There is no automatic fallback from a custom variable name to an unrelated built-in variable; `DBT_CLOUD_ENVIRONMENT_NAME` is populated separately and is never substituted for a different variable's missing value.
- D. dbt Cloud resolves environment variables during compilation using stored configuration, not interactive prompts, so no input dialog appears mid-run regardless of the interface being used.
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') | ? }}"Values are always strings, so a numeric setting needs the int (or as_number) filter. as_bool would produce a boolean, which is the wrong type.
Source: docs.getdbt.comOutside 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.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.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.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.
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.
“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.
“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.
“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.https://docs.getdbt.com/guides/dbt-migration-1Official docs
“we recommend using environment variables as the preferred method”
↩︎ Making jobs and connections environment-aware