What you will be able to do
- Predict whether dbt clone creates a zero-copy clone or a pointer view for a given warehouse and relation
- Use --state, --select, --full-refresh and --threads with dbt clone correctly
- Choose between dbt clone and deferral for development, BI testing and blue/green deployment
- Clone pre-existing incremental models as the first step of a slim CI job, and explain why state:old belongs in the selector
Key concept
Clone materialization — dbt clone reads a manifest from another environment and recreates the selected nodes in your current target. Where the warehouse supports zero-copy cloning and the source object is a table, dbt creates a real clone. Everywhere else it creates a view that selects from the source object.
1.What dbt clone creates in your target
dbt clone copies objects from one environment into another without running any model SQL. It arrived in dbt v1.6, alongside dbt retry, as part of a set of features for running mature deployments at scale. You point it at the artifacts of another run with --state, typically the manifest.json from a production job. dbt then works out where each selected node lives in that environment and creates a matching object in the schema(s) of your current target. The command uses a dedicated clone materialization, and the object it produces depends on two things: the warehouse you are on and what the source object is.
Zero-copy cloning only happens when both conditions hold. The warehouse must support zero-copy cloning of tables (Snowflake, Databricks or BigQuery), and the node must exist as a table in the source environment. In that case dbt creates a clone that shares the underlying storage, so no data is duplicated. If either condition fails, for example on a warehouse without zero-copy cloning or for a model that is a view in production, dbt creates a pointer view: select * from the source object. A pointer view is cheap to create, but every query against it still reads the production relation.
| Situation | Object created in your target |
|---|---|
| Snowflake, Databricks or BigQuery, and the model is a table in the source environment | Zero-copy clone of the table |
| Warehouse without zero-copy cloning of tables | Pointer view (select * from the source object) |
| Model is not a table in the source environment | Pointer view (select * from the source object) |
Checkpoint 1 of 9· Check yourself
An analytics engineer runs dbt clone --state prod-artifacts into their dev schema on a warehouse that does not support zero-copy cloning. In production, fct_orders is a table. What does dbt create in the dev schema for fct_orders?
Zero-copy cloning needs warehouse support as well as a table in the source environment. If the warehouse lacks that support, dbt falls back to a pointer view.
“Otherwise, dbt will create a simple pointer view (select * from the source object)”Source: docs.getdbt.com
By default, dbt clone leaves existing relations alone. If your dev schema already contains fct_orders from last week's work, a plain dbt clone --state ... skips it and clones only what is missing. That default protects work in progress, but it also means stale objects stay stale. To replace everything in the selection with a fresh clone of the source state, add --full-refresh. Selection works the same way it does for other commands: --select limits the clone to particular nodes. Each clone statement is independent of the others, so raising --threads well above your usual run setting shortens a large clone.
# clone all of my models from specified state to my target schema(s)
dbt clone --state path/to/artifacts
# clone one_specific_model of my models from specified state to my target schema(s)
dbt clone --select "one_specific_model" --state path/to/artifacts
# clone all of my models from specified state to my target schema(s) and recreate all pre-existing relations in the current target
dbt clone --state path/to/artifacts --full-refresh
# clone all of my models from specified state to my target schema(s), running up to 50 clone statements in parallel
dbt clone --state path/to/artifacts --threads 50Checkpoint 2 of 9· Check yourself
Your dev schema already contains a week-old dim_customers. You run dbt clone --state prod-artifacts with no other flags. What happens to dim_customers?
dbt clone skips relations that already exist in the current target. Only --full-refresh makes it recreate them.
“By default, dbt clone will not recreate pre-existing relations in the current target. To override this, use the --full-refresh flag.”Source: docs.getdbt.com
Checkpoint 3 of 9· Exam question
A Snowflake-backed project's Slim CI job takes 40 minutes because a 200 GB incremental model with `on_schema_change: fail` runs a full refresh on every pull request, since the model does not yet exist in the PR-specific schema. The team wants CI to exercise the same incremental path that runs in production without rebuilding the full history each time. Which action addresses this while keeping the incremental behavior test accurate?
Correct answer: A — Run `dbt clone --select state:modified+,config.materialized:incremental,state:old` ahead of the build step, so the pre-existing relation lands in the PR schema and `is_incremental()` evaluates true.
- A. Cloning the pre-existing incremental relation into the PR schema before the build step means `is_incremental()` sees an existing target and compiles the merge logic instead of a full rebuild, so CI exercises the same code path that runs in production without copying the full history.
- B. The `full_refresh` config controls whether a model is force-rebuilt; it does not create a relation where none exists, so the PR schema would still be missing the target table and `is_incremental()` would still evaluate to false.
- C. Rebuilding the production job nightly does nothing for the PR-specific schema dbt CI builds into — that schema still starts empty for every pull request, so the incremental model still full-refreshes there.
- D. A full `table` rebuild for CI defeats the goal of testing the incremental path at all, and on a 200 GB model it does not meaningfully reduce cost compared to the full refresh the team is trying to avoid.
2.When to clone and when to defer
dbt clone is built on deferral but costs more than deferral. Deferral, covered with state selection in [state and deferral](lesson:7.1), rewrites the ref calls for unselected nodes so they point at production, and it creates nothing extra. dbt clone does create objects. It uses some warehouse compute and leaves new relations in your schema. In many cases deferral is the cheaper and simpler choice. Clone earns its cost when something outside dbt needs to find real objects in your schema.
Each of these situations needs real objects in the target:
- Blue/green continuous deployment, on warehouses that support zero-copy cloning of tables. - Cloning the current production state into development schemas, so a dev environment is populated without running the full pipeline. - Handling incremental models in CI jobs, on warehouses that support zero-copy cloning. - Testing code changes on downstream dependencies in a BI tool, which can only query objects that exist in the warehouse.
The BI case shows the difference most clearly. Deferral only changes how dbt compiles SQL, so a dashboard pointed at your dev schema finds no tables there. After a clone, the dashboard can query the dev schema directly. The same reasoning applies to promotion. Because clone creates lightweight copies across environments, it can move specific models from a staging environment into production.
Checkpoint 4 of 9· Check yourself
A developer wants to check that a dashboard still renders after changing dim_customers. The dashboard is pointed at their dev schema, which is empty apart from the modified model. Which approach lets the dashboard read every upstream table?
Deferral changes only how dbt resolves ref, so the BI tool would still find an empty schema. dbt clone creates actual warehouse objects that a tool outside dbt can query.
“dbt clone allows you to test out your code changes on downstream dependencies outside of dbt (such as a BI tool)”Source: docs.getdbt.com
How you set up defer depends on where you run clone. In the dbt platform CLI, dbt clone includes --defer automatically, so the command works with no extra setup. The Studio IDE needs three things first: a Production environment with at least one successful job run, the Defer to production toggle switched on in the lower-right corner of the command bar, and then the dbt clone command run from the command bar.
Checkpoint 5 of 9· Put it in order
Put the steps for running dbt clone in the Studio IDE in order
- 1.Run the dbt clone command from the command bar
- 2.Set up a Production environment and have a successful job run
- 3.Enable the Defer to production toggle in the command bar
The IDE needs a production run to defer to, and the defer toggle must be on, before dbt clone can resolve the source objects.
“Set up your Production environment and have a successful job run.”Source: docs.getdbt.com
Checkpoint 6 of 9· Exam question
A CI pipeline runs this command before building modified models on a Snowflake project: ``` dbt clone --select state:modified+,config.materialized:incremental --state path/to/prod/artifacts ``` A brand-new incremental model included in this pull request fails to clone with an error that no relation exists in production. Which part of the selection expression is causing this?
Correct answer: C — The command is missing `state:old`, so newly added incremental models with no production relation are included in the clone selection and fail.
- A. Narrowing the selector to `state:new` would drop the modified-but-pre-existing models the clone step is meant to pre-populate, and a purely new model still has no production relation to clone regardless of which state selector targets it.
- B. Removing the materialization filter would make the command try to clone non-incremental models too, which is unrelated to the error — the new model fails because no production relation exists for it, not because of the filter's scope.
- C. Without `state:old`, the selector matches every modified incremental model, including ones that only exist in this pull request; since `dbt clone` copies an existing production relation, a model with no counterpart in production has nothing to clone and errors out.
- D. The `--state` flag must point at a prior run's artifacts to clone from — usually production — not at the current project's own manifest; pointing it at the PR schema would give dbt nothing meaningfully different to copy.
3.Cloning incremental models in slim CI
A slim CI job builds into a fresh PR-specific schema, and incremental models do not handle that well. A typical job defers to production, runs dbt build --select state:modified+, and is triggered by every pull request against main. Suppose a PR modifies dim_wizards. The job builds dim_wizards and its downstream fct_orders into a temporary schema created for that PR.
That full rebuild causes two problems. Incremental models are usually the largest datasets in a project, so rebuilding them on every PR is slow and expensive. The rebuild also tests the wrong path. A full refresh can pass in CI even when the incremental run in production will fail after merge. The fix is to clone the pre-existing incremental models into the PR schema before the build runs. When dbt build starts, those models already exist, is_incremental is true, and CI runs the same code path that production will. This only works on warehouses with zero-copy cloning. Anywhere else, the clone step produces pointer views.
Checkpoint 7 of 9· Fill the gap
Which selector completes the clone step so that brand new incremental models are not cloned?
dbt clone --select state:modified+,config.materialized:incremental, ? state:old limits the intersection to nodes that already exist in the comparison manifest. New incremental models are skipped and run in full-refresh mode, just as they will in production.
Source: docs.getdbt.comThe comma-separated selector is an intersection, and each part has a job. state:modified+ picks modified models and everything downstream of them. config.materialized:incremental keeps only the incremental ones. state:old drops nodes that are new in this PR. A brand-new incremental model has nothing in production to clone, and it will run as a full refresh once merged, so CI should run it as a full refresh too. Without state:old, the command still works but logs "No relation found in state manifest for…". Adding state:old states the intent explicitly. The second step is the usual dbt build --select state:modified+.
Cloning also brings schema-change failures into CI, and you need to decide how to handle them. Consider an incremental model with on_schema_change='fail' and a PR that adds a column. An incremental build of that model fails, while a full refresh succeeds. With the clone step in place, CI fails the way production will after merge. The alternative is a green CI check followed by a failing nightly dbt build. dbt Labs prefers the failing CI job: someone manually overrides the branch protection rule and then runs dbt build --full-refresh --select my_incremental_model in production right after merge.
{{
config(
materialized='incremental',
unique_key='unique_id',
on_schema_change='fail'
)
}}Checkpoint 8 of 9· Check yourself
A PR adds a column to an incremental model configured with on_schema_change='fail'. CI clones the existing model before running dbt build. What should the team expect?
After the clone, the model already exists in the PR schema, so it runs incrementally. An incremental build with a new column fails under on_schema_change='fail', exactly as production would.
“An incremental build will fail.”Source: docs.getdbt.com
Checkpoint 9 of 9· Exam question
By default, when a target relation for a selected model already exists in the destination schema, what does `dbt clone` do with that relation?
Correct answer: A — Skips it and leaves the existing relation in the target schema untouched, requiring `--full-refresh` to drop and recreate it.
- A. The default behavior of `dbt clone` is to skip relations that already exist in the target schema, which keeps repeated clone runs cheap; `--full-refresh` is required to force dbt to drop and recreate them.
- B. Dropping and recreating on every run is only what happens when `--full-refresh` is passed explicitly — without that flag, an existing relation in the target schema is left alone.
- C. `dbt clone` does not merge rows into an existing relation; it either copies the source relation wholesale or skips it if one already exists, rather than reconciling row-level differences.
- D. dbt does not version relations with timestamp suffixes when cloning; an existing target relation is simply left in place unless `--full-refresh` tells dbt to replace it.
Sources3
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.dbt clone always produces zero-copy clones of production tables.Why is that wrong?
Zero-copy clones need both a warehouse that supports them (Snowflake, Databricks, BigQuery) and a table in the source environment. In every other case dbt creates a pointer view.
Covered in What dbt clone creates in your target
2.Running dbt clone again refreshes every object that is already in the dev schema.Why is that wrong?
Pre-existing relations are skipped by default. Only --full-refresh recreates them.
Covered in What dbt clone creates in your target
3.dbt clone is the cheaper replacement for --defer.Why is that wrong?
Clone uses compute and creates objects. Deferral is often cheaper and simpler, and clone is worth it only when real objects are needed, for example for a BI tool.
Covered in When to clone and when to defer
4.Cloning incremental models in CI makes them run incrementally on any warehouse.Why is that wrong?
The strategy depends on zero-copy cloning. On other warehouses the clone step creates pointer views instead.
Covered in Cloning incremental models in slim CI
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.https://docs.getdbt.com/reference/commands/cloneOfficial docs
“If your data platform supports zero-copy cloning of tables (Snowflake, Databricks, or BigQuery)”
↩︎ What dbt clone creates in your target“individual clone statements are independent of one another”
↩︎ What dbt clone creates in your target“Unlike deferral, dbt clone requires some compute and creation of additional objects in your data warehouse.”
↩︎ When to clone and when to defer“The dbt clone command in the dbt platform CLI automatically includes the --defer flag.”
↩︎ When to clone and when to defer“blue/green continuous deployment (on data warehouses that support zero-copy cloning tables)”
↩︎ When to clone and when to defer“The dbt clone command clones selected nodes from the specified state to the target schema(s).”
↩︎ Key concept“Otherwise, dbt will create a simple pointer view (select * from the source object)”
↩︎ Exam trap 1“By default, dbt clone will not recreate pre-existing relations in the current target. To override this, use the --full-refresh flag.”
↩︎ Exam trap 2“In many cases, deferral is a cheaper and simpler alternative to dbt clone.”
↩︎ Exam trap 3“Otherwise, dbt will create a simple pointer view (select * from the source object)”
↩︎ Checkpoint“By default, dbt clone will not recreate pre-existing relations in the current target. To override this, use the --full-refresh flag.”
↩︎ Checkpoint“dbt clone allows you to test out your code changes on downstream dependencies outside of dbt (such as a BI tool)”
↩︎ Checkpoint“Set up your Production environment and have a successful job run.”
↩︎ Checkpoint - 2.https://docs.getdbt.com/docs/dbt-versions/dbt-upgrade/Older%20versions/upgrading-to-v1.6Official docs
“Mechanisms to support mature deployment at scale (dbt clone and dbt retry)”
↩︎ What dbt clone creates in your target“promoting specific models from a staging environment into production”
↩︎ When to clone and when to defer - 3.
“Typically incremental models are your largest datasets, so they take a long time to build in their entirety”
↩︎ Cloning incremental models in slim CI“it technically works without specifying state:old but adding state:old is more explicit”
↩︎ Cloning incremental models in slim CI“Our preference would be to have the failing CI job and have to manually override the blocking branch protection rule”
↩︎ Cloning incremental models in slim CI“This strategy only works for warehouse that support zero copy cloning (otherwise dbt clone will just create pointer views).”
↩︎ Exam trap 4“it does not yet exist in the PR-specific schema and is_incremental will be false”
↩︎ Prediction“An incremental build will fail.”
↩︎ Checkpoint