Project repo
Mental Model
From the workspace edge inward, every asset lives in one Databricks Asset Bundle in one Git repo, owned by one team.
The bundle is the deployment boundary: it carries variables for catalog, schema, and warehouse per environment, so the same YAML deploys to dev, staging, and prod.
Create the repo now, empty of resources, and validate it before adding anything.
A bundle that fails validate with no resources is a config problem; the same failure after three resources land is a hunt.
Goal
A new Git repo containing a valid databricks.yml with dev, staging, and production targets, validating clean against dev.
Prerequisites
- Auth surface:
workspace. - Infra Setup complete: workspaces, a metastore, catalogs with medallion schemas, and governed object storage access.
- A configured Databricks CLI profile that reaches the dev workspace.
- A deployment service principal for staging and production (created during Infra Setup). Staging and prod run as this, not as a person.
Skill
databricks-dabs (databricks-agent-skills).
Read its references/bundle-structure.md before writing YAML.
Inputs
| Input | Source | How to obtain |
|---|---|---|
| Project name | Human | Becomes the bundle name and the repo name. Kebab-case, marketing-c360. |
| Repo location | Human | Where to create it, and whether they want a remote. Ask before creating anything on GitHub. |
| Target catalogs per environment | Human | From Catalogs. Usually dev, stg, prod, or the prefixed variants. |
| Target schema prefix | You derive | <project>, with _bronze / _silver / _gold appended per layer |
| Workspace host per environment | You derive | databricks account workspaces list -o json, then deployment_name |
| Deployment service principal | You derive | The SP created during Infra Setup. Staging and production run as this. |
Run
0. Auth precheck
Refuse to continue if the brief or prior pages do not name all of these:
- Databricks account id
- Workspace id
- Workspace host (
https://<deployment>.cloud.databricks.com) - Workspace CLI profile name
databricks auth profiles
databricks auth describe --profile <workspace-profile> -o json \
| jq '{host, account_id}'
databricks current-user me --profile <workspace-profile> -o json \
| jq '{id, userName}'
databricks metastores current --profile <workspace-profile> -o json \
| jq '{workspace_id, metastore_id}'
Expected:
<workspace-profile>showsValid=YESinauth profiles.auth describehostequals the named workspace host, andaccount_idequals the named Databricks account id.current-user mesucceeds with no auth error.metastores currentworkspace_idequals the named workspace id.
On any failure: print blocked: auth preflight failed, list the failing check, give the human the remediation below, and stop.
Do not invoke skills, run bundle validate, or deploy until auth is green.
| Check failed | Human remediation |
|---|---|
| Missing named account id, workspace id, host, or profile | Ask the human for all four before continuing |
Profile Valid=NO or auth error on describe |
databricks auth login --host <workspace-host> --profile <workspace-profile> (or refresh the SP OAuth secret on the profile) |
Host or account id mismatch on auth describe |
Re-login the profile against the named host; confirm the Databricks account id in the account console |
workspace_id mismatch on metastores current |
databricks account workspaces list --profile <account-profile> -o json and align id with the named host |
current-user me fails after profile is Valid |
Workspace admin assigns the user or SP to the workspace |
1. Structure
<project>/
├── databricks.yml bundle name, variables, targets
├── resources/
│ └── <name>.<type>.yml one file per resource, added by later pages
├── src/
│ └── ... pipeline source, notebooks, Python
├── tests/
│ └── ... unit tests, run by CI
└── .github/workflows/ added when CI lands
Resource files use <name>.<resource_type>.yml.
That naming is what databricks-dabs expects, and it keeps resources/ readable once a project holds a pipeline, jobs, a dashboard, and an agent.
2. The bundle
Parameterize catalog, schema, and warehouse as variables. Hardcoding them into resources is what makes a bundle undeployable to a second environment.
# databricks.yml
bundle:
name: <project>
variables:
catalog:
description: Unity Catalog catalog for this target
schema_prefix:
description: Schema name prefix, medallion suffixes appended per layer
default: <project>
warehouse_id:
description: SQL warehouse for SQL tasks and dashboards
include:
- resources/*.yml
targets:
dev:
mode: development
default: true
workspace:
host: https://<dev-workspace>.cloud.databricks.com
variables:
catalog: dev
staging:
mode: production
workspace:
host: https://<staging-workspace>.cloud.databricks.com
root_path: /Workspace/Shared/.bundle/${bundle.name}/${bundle.target}
run_as:
service_principal_name: <prefix>-deployer
variables:
catalog: stg
prod:
mode: production
workspace:
host: https://<prod-workspace>.cloud.databricks.com
root_path: /Workspace/Shared/.bundle/${bundle.name}/${bundle.target}
run_as:
service_principal_name: <prefix>-deployer
variables:
catalog: prod
mode: development prefixes resource names with the deploying user and pauses schedules, so two engineers can deploy to dev without colliding.
mode: production does neither, which is why staging and production must run as a service principal rather than as whoever deployed last.
3. Init and validate
mkdir <project> && cd <project>
git init
mkdir -p resources src tests
# write databricks.yml
databricks bundle validate --strict --target dev --profile <name>
git add -A && git commit -m "Bundle scaffold with dev, staging, prod targets"
--strict is the point.
Without it, unknown keys pass silently and surface as a confusing deploy failure later.
Verify
# Validates clean and resolves the variables you expect
databricks bundle validate --strict --target dev --profile <name> -o json \
| jq '{name: .bundle.name, target: .bundle.target, catalog: .variables.catalog.value}'
# Every target resolves, not just dev
for t in dev staging prod; do
echo -n "$t: "
databricks bundle validate --strict --target "$t" --profile <name> >/dev/null 2>&1 \
&& echo OK || echo FAIL
done
Expected text:
{"name":"<project>","target":"dev","catalog":"dev"}
dev: OK
staging: OK
prod: OK
A FAIL on staging or production here is usually the service principal not existing or not having workspace access, not a YAML error.
Run databricks bundle validate --target staging and read the message rather than guessing.
Where this fails
| Symptom | Cause | Fix |
|---|---|---|
| Auth precheck blocked: missing named targets | Brief only has a workspace URL or display name | Collect Databricks account id, workspace id, workspace host, and workspace profile name before Run |
Profile Valid=NO |
Expired OAuth or SP secret | databricks auth login --host <workspace-host> --profile <workspace-profile> or rotate the SP secret |
Account id or host mismatch on auth describe |
Profile points at the wrong account or workspace | Re-login against the named host; confirm account id in the account console |
workspace_id mismatch on metastores current |
Wrong profile or wrong workspace in the brief | List workspaces and align id, host, and profile |
current-user me fails with Valid profile |
Principal not on the workspace | Workspace admin assigns the user or SP |
unknown command "bundle" |
Legacy pip CLI on PATH | Install the modern Databricks CLI |
cannot resolve variable |
Variable used in a resource but not declared in databricks.yml |
Declare it under variables: |
| Validate passes on dev, fails on prod | Target missing a variable value, or the SP has no workspace access | Set the variable per target; assign the SP to the workspace |
| Two engineers overwrite each other in dev | Dev target not in mode: development |
Set it. Development mode prefixes resources per user. |
| Paths resolve differently than expected | Relative paths resolve from the file that declares them, not the bundle root | Read references/bundle-structure.md on path resolution |
Next
- Do next: Ingestion Pipelines