When a nightly dbt run balloons to three hours, the fix begins with profiling data rather than randomly editing queries. The resolution path follows a systematic order: profile run artifacts to locate the bottleneck, optimize model materializations and joins, adjust execution concurrency, and split heavy workloads onto separate schedules.
Triage and profiling with run artifacts
Start by inspecting target/run_results.json from the latest production run, or check the model timing breakdown in the dbt Cloud dashboard. Sort all models by execution time descending.
In almost every slow project, the Pareto principle applies: out of several hundred models, five to ten models account for more than two hours of the total runtime.
Identify whether the delay is caused by:
- A single massive model bottlenecking the entire DAG while other threads sit idle.
- Hundreds of small models waiting in queue due to low thread concurrency.
- Deep dependency chains where models must run sequentially.
If the evidence shows that execution is evenly distributed across hundreds of small models, increasing warehouse threads or cluster sizing is the appropriate move. If five models consume eighty percent of the time, focus engineering effort strictly on those models.
Architecture and materialization fixes
For the slowest identified models, inspect their SQL logic and warehouse execution plans:
- Convert full refreshes to incremental models on large fact tables with tens of millions of rows that rebuild from scratch every night.
- Add partitioning and clustering to large tables on Snowflake, BigQuery, or Databricks so incremental merges prune historical data.
- Eliminate deeply nested views that stack six levels deep and force the query planner to recompute complex joins repeatedly.
- Fix join fan-outs and cartesian products where output rows vastly exceed input rows, causing warehouse memory spills to disk.
Refactoring these specific bottlenecks usually yields the largest runtime reductions.
Execution schedule and concurrency adjustments
Once model SQL is optimized, adjust project-level execution mechanics:
- Increase thread concurrency in profiles.yml. Bumping threads from 4 to 8 or 16 allows independent DAG branches to execute in parallel.
- Move heavy monthly reports, executive aggregations, and feature store models to separate, dedicated schedules rather than running them in the primary nightly job.
- Query warehouse access logs to identify models with zero read queries over the past ninety days, then archive and remove them from the project.
To stop runtimes from creeping up in the future, implement a CI performance check that alerts the team whenever a pull request adds a model taking longer than five minutes to build.