dbt Job Scheduling: Running Production Builds Reliably in dbt Cloud
Every command covered throughout this series โ dbt build, dbt test, dbt snapshot, dbt docs generate โ needs to actually run on a schedule in production, not just when someone remembers to trigger it manually. dbt Cloudโs job scheduling (or an equivalent orchestration setup, as covered in Artifacts in Orchestration) is where all of this becomes a dependable, recurring pipeline instead of an ad hoc set of commands someone runs by hand.
Anatomy of a dbt Cloud Job
A job in dbt Cloud bundles together the commands to run, the schedule to run them on, and the environment (warehouse credentials, dbt version) to run them in.
Job: Production Daily BuildCommands: 1. dbt source freshness 2. dbt build --exclude resource_type:snapshot 3. dbt docs generateSchedule: Daily at 06:00 UTCEnvironment: ProductionThis mirrors the command sequencing covered in dbt build โ freshness first as a fail-fast gate, then the interleaved build-and-test pass, then documentation regeneration as the final step.
Separating Snapshot Jobs From the Main Build
As covered in dbt snapshot, snapshots have their own scheduling considerations โ their frequency directly determines historical granularity, and theyโre usually excluded from the main build/test job so their cadence can be tuned independently.
Job: Hourly Snapshot CaptureCommands: 1. dbt snapshotSchedule: Every hour
Job: Daily Production BuildCommands: 1. dbt build --exclude resource_type:snapshotSchedule: Daily at 06:00 UTC (runs after the last hourly snapshot)Running these as genuinely separate jobs, rather than trying to force one schedule to serve both purposes, is almost always the right call โ snapshot granularity needs are rarely the same as how often you want the rest of the project rebuilt.
Scheduling by Cron Expression
Beyond simple daily/hourly presets, dbt Cloud (and most orchestration tools) support standard cron syntax for more specific timing needs.
0 6 * * 1-5 # 6 AM UTC, weekdays only*/15 * * * * # Every 15 minutes0 2 * * 0 # 2 AM UTC, Sundays only (weekly maintenance)A common real pattern: a frequently-updating source (order events) justifies a 15-minute build cadence for the models directly consuming it, while a full-project rebuild including expensive historical aggregations only needs to run once nightly.
Job Dependencies: Chaining Multiple Jobs
Rather than one monolithic job doing everything, larger projects often chain multiple jobs with explicit dependencies โ a documentation regeneration job that only triggers after the main build job succeeds, for instance.
Job A: "Production Build" โ triggered on scheduleJob B: "Generate Docs" โ triggered on successful completion of Job AJob C: "Notify Slack" โ triggered on completion of Job A (success or failure)This keeps failure handling clean โ if the build fails, dependent jobs like doc regeneration correctly donโt run against a possibly broken state, while notification jobs still fire regardless of outcome so the team knows what happened either way.
Alerting on Job Failure
A scheduled job that fails silently, with nobody noticing until a stakeholder asks why a dashboard looks wrong, defeats most of the value of scheduling in the first place. Wiring failure notifications directly into the job โ Slack, email, PagerDuty for genuinely critical pipelines โ closes that gap.
Job: Production Daily BuildOn Failure: Post to #data-eng-alerts Slack channelOn Failure (if severity=critical): Page on-call via PagerDutyEnvironment Separation: Dev, Staging, Production Jobs
Distinct scheduled jobs, each pointed at its own environment and credentials, keep development iteration from ever accidentally touching production data.
| Environment | Typical schedule | Purpose |
|---|---|---|
| CI | On every pull request | Validate changes before merge, covered in dbt CI/CD Integration |
| Staging | Nightly, or on merge to a staging branch | Pre-production validation against realistic data |
| Production | Scheduled per business need (hourly, daily) | The actual pipeline stakeholders depend on |
Monitoring Job History Over Time
Beyond individual run success/failure, reviewing job run history over weeks reveals trends worth acting on โ a build thatโs been slowly getting slower, a test that fails intermittently rather than consistently (often a sign of a race condition or a genuinely flaky upstream source rather than a deterministic bug).
# Using archived run_results.json across multiple runs, as covered# in Artifacts in Orchestration, to track execution time trendsCommon Mistakes
Running everything in one monolithic job with no separation of concerns. Snapshots, builds, and documentation generation have genuinely different scheduling needs and failure implications โ bundling them all into one undifferentiated job makes tuning any single piece harder.
No failure alerting configured. A scheduled job silently failing for days, with no one noticing until a stakeholder complains, is one of the most common and most avoidable operational gaps in a dbt deployment.
Not separating CI, staging, and production schedules and credentials. Sharing environments across these purposes risks CI or development runs accidentally affecting production data or vice versa.
Summary
| Practice | Why it matters |
|---|---|
| Separate snapshot and build jobs | Different, independently-tunable scheduling needs |
| Explicit job dependencies | Prevents downstream jobs (docs, notifications) from running against a broken build |
| Failure alerting | Turns a silent pipeline failure into an immediately actionable incident |
| Environment-separated jobs | Prevents CI/dev activity from ever touching production |
Job scheduling is where every other practice covered in this series โ testing, freshness, snapshots, documentation โ actually becomes a dependable, recurring production pipeline rather than a set of commands someone has to remember to run.