What you will be able to do
- Work out which value a flag takes when it is set by CLI option, environment variable, dbt_project.yml and user_settings.yml
- Identify which flags can and cannot be set in the flags dictionary of dbt_project.yml
- Opt in or out of a behavior change flag and handle the warnings that follow
- Read flag values such as flags.FULL_REFRESH in Jinja without breaking parsing
Key concept
Flag precedence — A flag (global config) can be set in up to four places. When the same flag is set in more than one, the most specific setting wins: CLI option, then environment variable, then dbt_project.yml, then user_settings.yml, then dbt's built-in default.
1.Where flags are set and which setting wins
Flags change how dbt runs, not what it runs. Selecting models and setting materializations is the job of resource configs. Flags, also called global configs, control things like log output, whether warnings become errors, and whether a run stops at the first failure. They are called global because every dbt command accepts them and they can be set in several places. That is why debugging them is tricky. If a run behaves differently in CI than on your laptop, the flag may come from a place you didn't check.
Each of the four places has its own purpose.
- CLI options set behavior for a single invocation.
- Environment variables vary behavior by runtime environment (development, production, CI) or by developer.
- Project-level flags in the flags: dictionary of dbt_project.yml are version-controlled defaults for everyone who runs the project. They are also where you opt in to or out of behavior changes.
- User settings in ~/.dbt/user_settings.yml hold personal preferences that apply to every project on your machine. dbt login writes this file.
The most specific setting wins. If none of the four sets the flag, dbt uses its built-in default. The fail_fast flag shows the pattern.
# dbt_project.yml
flags:
# set default for running this project -- anywhere, anytime, by anyone
fail_fast: true
# set this environment variable to 'True' (bash syntax)
export DBT_ENGINE_FAIL_FAST=1
dbt run
dbt run --fail-fast # set to True for this specific invocation
dbt run --no-fail-fast # set to FalseThe environment variable prefix depends on the version. dbt v1.11 and later use DBT_ENGINE_, and v1.10 and earlier use DBT_. If an old CI script sets a flag with the old prefix after an upgrade, check that the variable name matches the version you run. Boolean flags come in pairs: --fail-fast turns the flag on and --no-fail-fast turns it off. Non-boolean flags take a value, as in --printer-width=80.
Put CLI flags after the subcommand. Write dbt run --no-populate-cache, not dbt --no-populate-cache run. Passing flags before the subcommand is legacy behavior that dbt Labs can remove at any time, and passing the same flag on both sides of the subcommand is not supported. User settings sit at the bottom of the order. If user_settings.yml sets manage_state: true and you run dbt run --no-manage-state, the CLI option wins for that run.
Checkpoint 1 of 8· Check yourself
A developer has manage_state: true in ~/.dbt/user_settings.yml. The project's dbt_project.yml does not set manage_state. They run dbt run --no-manage-state. What happens to dbt State for this invocation?
user_settings.yml has the lowest precedence and a CLI option has the highest, so --no-manage-state disables dbt State for that run.
“if manage_state: true is set in user_settings.yml but you run dbt run --no-manage-state, the CLI flag takes precedence”Source: docs.getdbt.com
Checkpoint 2 of 8· Exam question
A CI job exports the environment variable `DBT_ENGINE_FAIL_FAST=1` in its shell before running `dbt build --no-fail-fast --select my_model`. Which behavior does dbt apply for this single invocation, and why?
Correct answer: A — dbt continues past a failing node instead of stopping, because a CLI option on this invocation outranks an environment variable for the same flag.
- A. CLI options apply to the single invocation that passes them and sit at the top of dbt's flag precedence order, so `--no-fail-fast` overrides the environment variable for this run.
- B. This reverses the actual order: environment variables sit below CLI options in precedence, so an env var cannot override a flag explicitly passed on the command line.
- C. dbt does not error out when a flag is set from more than one source; it silently resolves the value using the documented precedence order instead of refusing to run.
- D. `fail_fast` is an ordinary global flag settable from CLI, environment variable, or `dbt_project.yml`, not one of the small set of behavior-change flags restricted to the project file.
2.What the flags dictionary can and cannot hold
You can set most flags in all of these places, but there are two important exceptions. The first is file paths. Flags such as log_path and state point at locations used during execution, and you cannot set them in dbt_project.yml. To change them, pass a CLI option such as --log-path or --state, or set an environment variable such as DBT_ENGINE_LOG_PATH. Settings that tell dbt where to find project resources, like model-paths, do go in dbt_project.yml. They are top-level keys outside the flags: dictionary, because they are meant to stay fixed whatever command or environment runs.
The second exception runs the other way, and the next section covers it. Behavior change flags can only be set in dbt_project.yml. Some flags also work differently from the rest. empty is a CLI option or environment variable and has no project setting. full_refresh and store_failures can appear in the project only as resource configs, not as flags: entries. To check a flag before you put it in a file, look at the 'In project?' column of the flags table.
| Flag | In dbt_project.yml? | Env var | CLI option |
|---|---|---|---|
| fail_fast | Yes | DBT_ENGINE_FAIL_FAST | --fail-fast / -x / --no-fail-fast |
| log_path | No | DBT_ENGINE_LOG_PATH | --log-path |
| state | No | DBT_ENGINE_STATE | --state |
| empty | No | DBT_ENGINE_EMPTY | --empty / --no-empty |
| full_refresh | Yes (as resource config) | DBT_ENGINE_FULL_REFRESH | --full-refresh / --no-full-refresh |
| warn_error_options | Yes | DBT_ENGINE_WARN_ERROR_OPTIONS | --warn-error-options |
Checkpoint 3 of 8· Match them up
Match each setting to the only kind of place it can be set
Tap a term, then the definition that fits it.
Runtime file paths are kept out of the project file, resource locations are static top-level keys, and behavior change flags are kept in version control.
“Flags for file paths that are relevant to runtime execution (for example, --log-path or --state) cannot be set in dbt_project.yml.”Source: docs.getdbt.com
Sources1
3.Behavior change flags: opting in, opting out, and handling the warnings
A behavior change is a deliberate update that makes the same project code and the same commands give a different result. Examples include a new validation error, a changed signature on a built-in macro that your project reimplemented, or an adapter method removed from the {{ adapter }} Jinja object. dbt puts each of these behind a flag so you decide when to adopt it. A bug fix doesn't count as a behavior change. Neither does a new warning or reworded log text. When an upgrade makes a project that used to work start failing validation, a behavior change flag is a likely cause.
Every behavior change flag goes through three phases. When introduced, dbt supports both the old and new behavior, and the flag is disabled by default. When mature, the default switches to the new behavior. You can still keep the old behavior, but you may see deprecation warnings. When removed, the old behavior is gone from the codebase. Several flags are marked as removed in dbt v2, where the new behavior is always on. For example, skip_nodes_if_on_run_start_fails can stop a build, and state_modified_compare_more_unrendered_values changes which nodes get selected, which can affect CI.
Checkpoint 4 of 8· Put it in order
Put the phases of a behavior change flag in order
- 1.Mature: the default switches to the new behavior, and the old one is kept with deprecation warnings
- 2.Removed: the old behavior is deleted from the dbt codebase
- 3.Introduced: both behaviors exist and the new one is disabled by default
A flag starts disabled, then becomes the default, and finally its old behavior is removed.
“Mature (enabled by default): The default value of the flag is switched to the new behavior by default.”Source: docs.getdbt.com
You can set these flags only in the flags: dictionary of dbt_project.yml. No environment variable or CLI option sets them. They are tied closely to project code, so a change goes through a pull request with the same testing and peer review as any other code change, and the value should not differ between environments or users for long. To opt out, set the flag to false. Opting out does not silence anything. You keep seeing warnings for the legacy behavior, and you keep that behavior even after the maturity date, until you either migrate and set the flag to true or silence the warning.
Two flags control how warnings are handled. warn_error turns every warning into an error. warn_error_options gives you finer control through three parameters: error lists the warnings to promote to errors, warn keeps listed warnings as warnings when error is all, and silence ignores warnings. The two flags are mutually exclusive. Setting both, even in different places such as an environment variable and a CLI flag, gives a usage error. Be careful with --warn-error in production: a warning added in a future dbt version will then fail your job. List only the warnings you mean to promote.
Checkpoint 5 of 8· Fill the gap
Which parameter of warn_error_options ignores the listed warning?
flags:
warn_error_options:
error: # Previously called "include"
warn: # Previously called "exclude"
? : # To silence or ignore warnings
- NoNodesForSelectionCriteriasilence drops the warning. warn only keeps a warning from being promoted when error is set to all, and exclude is the old name for warn.
Checkpoint 6 of 8· Exam question
The team wants `require_explicit_package_overrides_for_builtin_materializations` enabled for every developer and every CI job, tracked in version control, with no way for a single run to opt out. Where should this flag be configured?
Correct answer: A — Add it under the `flags:` key in `dbt_project.yml`, since behavior-change flags are accepted only there, never from CLI options or environment variables.
- A. Behavior-change flags are deliberately restricted to the `flags:` block of `dbt_project.yml` so the setting is version-controlled, shared by every contributor, and cannot be overridden per run.
- B. Behavior-change flags cannot be set through an environment variable at all, and even if they could, an env var is scoped to whichever shell or job exports it rather than shared project-wide.
- C. A CLI option only affects the single invocation that passes it, and behavior-change flags are not accepted as CLI options in the first place, so this would not enforce a shared, unchangeable default.
- D. `user_settings.yml` is a personal, machine-local default for one developer across all of their projects, the opposite of a project-wide setting every contributor inherits.
4.Reading flag values in Jinja
Your own Jinja can read flags through the flags context variable. Use it when logic should depend on how dbt was invoked. Common cases are materialization logic for different run modes, which read flags.FULL_REFRESH, flags.STORE_FAILURES and flags.EMPTY, and hooks that run only for certain commands, which read flags.WHICH. A materialization that drops and rebuilds on a full refresh checks the flag directly. What --empty does to a model is covered in [dry runs with --empty](lesson:1.12). Here, flags.EMPTY is just a value your code can test.
{% if flags.FULL_REFRESH %}
drop table ...
{% else %}
-- no-op
{% endif %}# dbt_project.yml
on-run-start:
- '{{ log("I will stop at the first sign of trouble", info = true) if flags.FAIL_FAST }}'Use flags only in code that runs at execution time. Don't use them in anything dbt resolves while parsing, such as configs, properties, or ref and source dependencies. Flag values can change on every invocation. With partial parsing on, dbt reuses earlier parse results, so a value read at parse time goes stale and gives wrong results on later runs. If a config seems to keep the full-refresh setting from an earlier run, look for a flag read at parse time.
Checkpoint 7 of 8· Check yourself
A developer writes materialized = 'table' if flags.FULL_REFRESH else 'incremental' in a model's config() call. Partial parsing is on. What is the risk?
Configs are resolved at parse time. With partial parsing, the parsed flag value can be reused on later invocations where the flag has changed.
“their parsed values will become stale (and yield incorrect results) in subsequent invocations that have partial parsing enabled”Source: docs.getdbt.com
Checkpoint 8 of 8· Exam question
A developer runs `dbt run --full-refresh --select my_model` and expects the log line below to print, but nothing appears in the output. ``` {% macro conditional_rebuild() %} {% if flags.full_refresh %} {{ log("Rebuilding from scratch", info=true) }} {% endif %} {% endmacro %} ``` Which part of this macro is at fault?
Correct answer: A — `flags.full_refresh` is spelled wrong; the `flags` context exposes this as `flags.FULL_REFRESH`, so the lowercase name resolves to an undefined, falsy value.
- A. The Jinja `flags` context exposes attributes in uppercase, matching the CLI flag name, so `flags.FULL_REFRESH` holds the real value while the lowercase `flags.full_refresh` is undefined and evaluates as falsy.
- B. The `flags` context is populated from the CLI invocation and is readable inside any macro, including ones used by non-incremental models, so it is available here.
- C. `log()` accepts `info` as a keyword argument and this usage is correct; the argument style is not what is preventing the message from appearing.
- D. There is no `full_refresh()` function in dbt; reading flag values through the `flags` Jinja variable, as the macro already attempts, is the documented approach.
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.You can opt in to a behavior change for one CI run with an environment variable or a CLI option.Why is that wrong?
Behavior change flags can only be set in the flags dictionary of dbt_project.yml, so changes go through version control and review.
Covered in Behavior change flags: opting in, opting out, and handling the warnings
2.Setting a behavior change flag to false makes its deprecation warnings go away.Why is that wrong?
Opting out keeps the legacy behavior, but the warnings continue until you migrate and set the flag to true, or silence them with warn_error_options.silence.
Covered in Behavior change flags: opting in, opting out, and handling the warnings
3.log_path or state can be set under flags: in dbt_project.yml like any other default.Why is that wrong?
Runtime file-path flags are excluded from dbt_project.yml. Set them with a CLI option or an environment variable.
4.--warn-error and --warn-error-options can be combined, for example one in an environment variable and one on the command line.Why is that wrong?
The two are mutually exclusive wherever they are set. Specifying both gives a usage error.
Covered in Behavior change flags: opting in, opting out, and handling the warnings
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“settings for fine-tuning how dbt runs your project. They differ from resource-specific configs that tell dbt what to run.”
↩︎ Where flags are set and which setting wins“If you set the flag in none of those places, it will use the default value defined within dbt.”
↩︎ Where flags are set and which setting wins“In general, v1.10 and earlier use the DBT_ prefix, while v1.11+ uses the DBT_ENGINE_ prefix.”
↩︎ Where flags are set and which setting wins“are set in dbt_project.yml, but as a top-level key, outside the flags dictionary”
↩︎ What the flags dictionary can and cannot hold“Flags for file paths that are relevant to runtime execution (for example, --log-path or --state) cannot be set in dbt_project.yml.”
↩︎ What the flags dictionary can and cannot hold“Custom user-defined logic, written in Jinja, can check the values of flags using the flags context variable.”
↩︎ Reading flag values in Jinja“we strongly advise against using flags as an input to configurations or dependencies (ref + source) that dbt resolves during parsing.”
↩︎ Reading flag values in Jinja“CLI options take the highest precedence, followed by environment variables, then dbt_project.yml, and finally user_settings.yml.”
↩︎ Key concept“Flags opting in or out of behavior changes can only be defined in dbt_project.yml.”
↩︎ Exam trap 1“Flags for file paths that are relevant to runtime execution (for example, --log-path or --state) cannot be set in dbt_project.yml.”
↩︎ Exam trap 3“CLI options take the highest precedence, followed by environment variables, then dbt_project.yml, and finally user_settings.yml.”
↩︎ Prediction - 2.
“We do not support using the same flag before and after the subcommand.”
↩︎ Where flags are set and which setting wins - 3.
“user_settings.yml is the lowest precedence level in dbt's flag hierarchy.”
↩︎ Where flags are set and which setting wins“if manage_state: true is set in user_settings.yml but you run dbt run --no-manage-state, the CLI flag takes precedence”
↩︎ Checkpoint - 4.
“A behavior change is a deliberate update to dbt where the same project code and commands produce a different result than before”
↩︎ Behavior change flags: opting in, opting out, and handling the warnings“It is not a bug fix, a new warning, or a non-breaking addition.”
↩︎ Behavior change flags: opting in, opting out, and handling the warnings“These flags must be set in the flags dictionary in dbt_project.yml.”
↩︎ Behavior change flags: opting in, opting out, and handling the warnings“To opt out of a specific behavior change, set the value of the flag to false in dbt_project.yml.”
↩︎ Behavior change flags: opting in, opting out, and handling the warnings“Explicitly set the flag to false. You will continue to see warnings, and you will retain the legacy behavior even after the maturity date”
↩︎ Behavior change flags: opting in, opting out, and handling the warnings“You will continue to see warnings for legacy behaviors you've opted out of, until you either:”
↩︎ Exam trap 2“Mature (enabled by default): The default value of the flag is switched to the new behavior by default.”
↩︎ Checkpoint - 5.
“if a new warning is introduced in a future version of dbt, your production job may start failing unexpectedly.”
↩︎ Behavior change flags: opting in, opting out, and handling the warnings“warn tells dbt to continue treating the warnings as warnings.”
↩︎ Behavior change flags: opting in, opting out, and handling the warnings“WARN_ERROR and WARN_ERROR_OPTIONS are mutually exclusive.”
↩︎ Exam trap 4 - 6.
“flags.FULL_REFRESH, flags.STORE_FAILURES, and flags.EMPTY”
↩︎ Reading flag values in Jinja“running hooks conditionally based on the current command / task type, via flags.WHICH”
↩︎ Reading flag values in Jinja“their parsed values will become stale (and yield incorrect results) in subsequent invocations that have partial parsing enabled”
↩︎ Checkpoint