What you need before running anything in this repo, and how to wire credentials.
- Unity Catalog enabled, with a catalog you can write to (default in examples:
workspace). - A SQL warehouse (Serverless or Pro). Both dlt and dbt connect through it via its HTTP path.
- Permission to create schemas and tables in your catalog. dlt creates the raw schema
(default
raw); dbt creates the analytics schema (defaultanalytics).
SQL Warehouses → select your warehouse → Connection details:
- Server hostname →
DATABRICKS_HOST(withouthttps://) - HTTP path →
DATABRICKS_HTTP_PATH(e.g./sql/1.0/warehouses/abcd1234efgh5678)
One-time setup — no PAT, no secrets in files:
databricks auth login --host https://YOUR_HOST.cloud.databricks.com
# browser opens → login → done. Credentials saved to ~/.databrickscfg.- dbt:
auth_type: oauthinprofiles.ymlreads~/.databrickscfgautomatically. Runuv run dbt debug— no env vars, no browser popup after first login. - dlt: still needs a PAT (see below). dlt doesn't yet read
~/.databrickscfg.
User Settings → Developer → Access tokens → Generate new token. Put it in
.env as DATABRICKS_TOKEN and DESTINATION__DATABRICKS__CREDENTIALS__ACCESS_TOKEN.
dlt loads .env automatically via python-dotenv.
Replace the PAT/U2M token with a service principal for any non-local workload:
- dlt: set
client_id/client_secretiningestion/.dlt/secrets.toml(env vars:DESTINATION__DATABRICKS__CREDENTIALS__CLIENT_ID/__CLIENT_SECRET). - dbt: add
client_id:/client_secret:env vars toprofiles.ymlprod target (see theprod:block inprofiles.yml.example).
cp .env.example .env
# fill: DATABRICKS_HOST, DATABRICKS_HTTP_PATH, DATABRICKS_TOKEN, DATABRICKS_CATALOG, DATABRICKS_SCHEMA
cp transformation/dbt_databricks/profiles.yml.example transformation/dbt_databricks/profiles.yml
# profiles.yml reads the same env vars, so usually no edits neededThe Python scripts call load_dotenv(), so .env is picked up automatically under uv run.
dlt also accepts the same values from ingestion/.dlt/secrets.toml (copy from the .example).
dlt 1.30 treats its entire credentials section as secret-bearing, so these values belong in
environment variables or secrets.toml, not config.toml. The examples leave
session_timezone unset because serverless SQL warehouses can reject the corresponding
spark.sql.session.timeZone session configuration; timestamp-producing examples use explicit
UTC-aware values instead.
| Layer | Schema (default) | Created by | Set via |
|---|---|---|---|
| Raw / landed | raw |
dlt | DLT_DATASET_NAME |
| Models (staging→marts) | analytics |
dbt | DATABRICKS_SCHEMA |
dbt's sources.yml points at the dlt raw schema — that's the handoff. Keep the two schema names
consistent across .env and profiles.yml.
uv sync
uv run python -c "import dlt, dbt; print('imports ok')"
make doctor # offline readiness check for env, dbt parse, CLI, and bundle config
make dbt-parse # parses dbt project; needs profiles.yml but does not write tables
make dlt-rest # first real write: loads a small REST API dataset into <catalog>.rawIf dlt-rest succeeds you'll see the new tables under your catalog's raw schema in the
Databricks Catalog explorer.
Asset Bundles read the workspace host from Databricks CLI auth, DATABRICKS_HOST, or an explicit
profile. Do not put ${var.workspace_host} in workspace.host; the CLI treats that field as
authentication configuration and does not support variable interpolation there.
export DATABRICKS_HOST=https://YOUR_HOST.cloud.databricks.com
databricks bundle validate --var warehouse_id=YOUR_WAREHOUSE_IDFor the full deploy path, see deploy-databricks-bundle.md.
For file or bulk loads dlt stages data before COPY INTO. On Databricks the simplest staging is a
Unity Catalog Volume. Create one (CREATE VOLUME <catalog>.<schema>.dlt_staging;) and reference
it from ingestion/.dlt/config.toml. The REST/SQL examples here are small enough to load directly.
For push-based append workloads, ingestion/advanced/zerobus_append.py demonstrates Databricks
Zerobus instead of object staging. It needs a supported region plus OAuth service-principal
credentials, a Zerobus endpoint, and a managed Delta table outside Databricks default storage; see
../ingestion/README.md for the exact target DDL
and least-privilege grants.
For a serverless staging investigation, compare SQL PUT and Files API uploads with the included probe before changing the loader.
- Windows
uv syncfails onruff.exewith OS error 5: an editor's Ruff server can keep.venv/Scripts/ruff.exeopen, preventing its replacement. This workspace usesruff.importStrategy: useBundledso VS Code runs the extension's server instead. If the old server is still running, restart Ruff from the Command Palette, then retryuv sync. Command-line lint and formatting continue to use the project's locked Ruff version. - 403 / permission denied → your principal lacks
USE CATALOG/CREATE SCHEMA/CREATE TABLE. - Zerobus “default storage” error → choose a catalog whose managed location is your own S3 storage. Zerobus cannot write to Databricks default storage.
- Zerobus Arrow schema mismatch on
_dlt_*→ pre-create the target with_dlt_load_idand_dlt_iddeclaredSTRING NOT NULL, as shown in the ingestion guide. - Warehouse won't start / timeouts → confirm the warehouse is running and the HTTP path matches it.
- dbt can't find profile → pass
--profiles-dir transformation/dbt_databricks(the Makefile does).