The Databricks pack: from resolved Ingestron definitions it generates a
Declarative Automation Bundle
(formerly Databricks Asset Bundle) with one
Lakeflow Spark Declarative Pipeline
per project. Name it in your standard: packs: [databricks@2].
What it uses on Databricks:
- Lakeflow Connect: SQL Server (through an ingestion gateway), Salesforce and Kafka, through Unity Catalog connections; only the table's columns, SCD type 1 or 2.
- Ingestion with Auto Loader: streaming tables over
STREAM read_files(…), with schema hints from the table's column types and a schema evolution mode. - Change data with AUTO CDC:
mode: upsert(SCD type 1) ormode: history(SCD type 2), keyed by the table's primary key. - Expectations: from required columns, the contract's rules and your own
checks, with
warn,droporfailper check, and a quarantine table for the rows a check drops. - Cleansing: trimming, empty text to null, mapped values, case, defaults
and time zone conversion in the table's
SELECT, and one row per key in a materialised view. - Liquid clustering:
CLUSTER BY AUTOby default, or named columns. - Materialised views for transforms, in SQL or PySpark.
- Serverless pipelines, a scheduled refresh job, Unity Catalog schemas and grants when you ask for them, permissions, tags and notifications.
- Access, when you turn it on: grants from the contracts' roles, and
column masks and row filters as Unity Catalog policies, which always let
run_asread real values.
One bundle covers every environment, with a target per environment, as
Databricks teams lay out bundles by hand. A value is a bundle variable only
where environments differ; everything the same in every environment is written
as it is. Each target sets its own variables, so the targets block shows
exactly what changes between dev and prod.
Most people use this through the ingestron command, which writes the bundle to build/databricks/. In code:
import { loadDefinitions, type ResolvedModel } from "@ingestron/definitions";
import { databricks } from "@ingestron/pack-databricks";
const models: Record<string, ResolvedModel> = {};
for (const environment of ["dev", "prod"]) {
const result = loadDefinitions("definitions", {
packs: [databricks],
environment,
deployTime: true,
environmentsTogether: true,
});
if (result.model) models[environment] = result.model;
}
const { files, problems } = databricks.buildEnvironments!(models);
for (const problem of problems) console.log(problem.code, problem.message);
for (const path of Object.keys(files)) console.log(path);For the retail example this prints:
databricks.yml
resources/retail.pipeline.yml
src/retail/bronze/orders.sql
src/retail/bronze/customers.sql
src/retail/silver/daily_sales.sql
resources/retail_refresh.job.yml
The values that differ between dev and prod are in databricks.yml:
variables:
catalog:
description: Catalog of the retail tables.
shop_path:
description: Folder the files of connection "shop" are read from.
targets:
dev:
mode: development
default: true
workspace:
host: https://adb-1111111111111111.1.azuredatabricks.net
# run_as and permissions, as the platform sets them
presets:
tags:
environment: dev
variables:
catalog: dev_sales
shop_path: abfss://landing@retaildev.dfs.core.windows.net/shop
prod:
mode: production
# prod's workspace, run_as, permissions, presets and variablesThe pipeline reads them as catalog: ${var.catalog} and as the configuration
value ingestron.shop_path, which its SQL uses as ${ingestron.shop_path}.
Tables in the pipeline's catalog are named with two parts, such as
bronze.orders.
From the bundle folder, deploy one environment at a time, the same files each time:
cd build/databricks
databricks bundle validate -t prod
databricks bundle deploy -t prodA value written as ${envvar:NAME} in the definitions is supplied when
deploying: the bundle declares it as a variable that the targets leave unset,
and the deploying CI pipeline sets BUNDLE_VAR_<name> (name is NAME in lower
case). A workspace host supplied this way is DATABRICKS_HOST.
prerequisites/<environment>.sql holds the grants an administrator runs once
in that environment. It is not deployed, and neither are .generated.json and
.generated.lock, which ingestron keeps in the folder.
The after-refresh task (tags, column comments, policies, grants and table
checks) is
tasks/<pipeline>/apply_contract.py, outside the pipeline's src/<pipeline>/
sources, so that only the refresh job runs it.
The pack reads the standard's intent: what a table keeps and how its data is
loaded, such as history: latest, written once for every engine. A project
that declares no intent gets the same files as with 2.2.
- Settings that are now other names for intent still work and build the same
files, but each is a
SETTING_MOVEDwarning with the fixWrite as intent:modeofstreaming-tableandlakeflow-connect:appendishistory: none,upsertishistory: latest,historyishistory: full;sequence_byofstreaming-tableissequence, anddelete_whenisload.deletes.when;cluster_by: autoandcluster_by: noneofstreaming-tableandmaterialised-viewarestorage.clustering: autoandnone. A list of columns stays a setting.
- The Platform has a
databrickssection.mode,workspace,run_as,compute,permissions,manageandgrantswritten at the top of the Platform still work, and each is aPLATFORM_FIELD_MOVEDwarning with the fixMove to databricks.<field>.notifications.on_failureis the same warning, with the fixMove to alerts, which writesalerts: [{ kind: email, to: [...], on: [failure] }]. The section also takeswarehouse_id. - New output appears only where the project declares it:
- the standard's
operations: retries on each pipeline task, the job's timeout, and its concurrency and queueing; - the Platform's
alerts: email and webhook alerts on failure, and, with the standard'soperations.alerts.slow_after, a run duration rule (Public Preview); quality.results: each pipeline's event log, published to a table, when the standard or any of the pipeline's tables keeps results;auditandrescued: load time, file path and modification time columns, and_rescued_data, in streaming tables that read files;cleanse: each column's operations in the table'sSELECT, anddeduplicateasQUALIFY row_number() … = 1in a materialised view;quality.quarantine: a<table>_quarantinetable of the rows adropcheck removes, with the failed checks' names in_failed_checks;databricks.manage.grants,masksandfilters: grants from the contracts' roles and layers, and column mask and row filter policies, made by the after-refresh task.
- the standard's
- Lakeflow Connect writes the table as the source gives it. Checks, cleansing
and deduplicate on such a table are
NOT_APPLIEDwarnings that say to do them in a transform in the next layer. An expectation written on the flow table is no longer an error. - Needs
@ingestron/definitions1.14 or later.
-
One bundle in
build/databricks/for every environment, in place of a bundle per environment inbuild/<environment>/. Change the standard todatabricks@2. -
The bundle name, pipeline and job keys, target names and
root_pathare the same as in 1.x, so the firstbundle deploy -t devor-t produpdates that environment's resources in place. Runbundle validateandbundle summaryfirst. -
The mode is
productionfor every environment exceptdev, unless the platform sets it. In 1.x, environments such astest,uatandstagingwere indevelopmentmode. On their first 2.0 deploy, jobs and pipelines lose the[dev <identity>]prefix in their names, pipelines are no longer in development, managed schemas lose theirdev_<identity>_prefix (the old schema is deleted after approval, and the pipelines write to a new one), and production mode wantsrun_asandpermissionsunless a service principal deploys. To keepdevelopmentmode for such an environment, writemode: ${values.mode}in the platform,mode: productionin project.yaml'svalues, andmode: developmentin that environment'svalues. -
Managed schemas are keyed by layer (
bronze), or by schema name when a layer has several (bronze_shop), not<catalog>_<schema>. Each environment's deployment state still holds the old key, so withmanage.schemas, before the first 2.0 deploy, for each environment and each managed schema:- in the 1.x folder
build/<environment>/, rundatabricks bundle deployment unbind <catalog>_<schema> -t <environment>; - in
build/databricks/, rundatabricks bundle deployment bind <key> <catalog>.<schema> -t <environment>, with the key fromresources/schemas.yml; - run
bundle validateandbundle summary, and check that the deploy deletes no schema.
Without this, the deploy plans to delete each schema under its old key.
- in the 1.x folder
-
The after-refresh script moved from
src/<pipeline>/apply_contract.py, where the pipeline also loaded it as its own code, totasks/<pipeline>/apply_contract.py. The first deploy removes the old file. -
Pipeline code names tables in the pipeline's catalog with two parts.
-
prerequisites.sqlisprerequisites/<environment>.sql. -
Environments may differ only in values. A table built in some environments only, or code that differs in more than a value, is
ENVIRONMENTS_DIFFER. A PySpark transform reads names that differ from the pipeline's configuration withspark.conf.get("ingestron.<name>"), into a module-level constant for its decorator; values in its own code cannot differ yet. -
Needs
@ingestron/definitions1.9 or later, and Databricks CLI v0.264.0 or later, which adds preset tags to pipelines.
The rules are in spec/behaviour.md.
Used with the ADF pack
(packs: [databricks@2, adf@2]), streaming tables read the files ADF lands,
from its landing folder.
Columns ADF adds to the landed rows (audit_columns in ADF 1.4) are read
with the files; timestamps, which ADF writes as text, are cast to TIMESTAMP.
@ingestron/definitions, a peer dependency: the resolved model.liquidjs: renders the SQL templates intemplates/and the transforms' SQL.yaml: writes the bundle's YAML files.
Bundle variables, targets, presets and pipeline configuration:
spec/references.md (accessed 2026-11-04). Bundle fields
from Databricks CLI v1.23.0 databricks bundle schema (2026-11-04).
Accessed 2026-10-24: databricks/bundle-examples (lakeflow_pipelines_sql).
SQL from
CREATE STREAMING TABLE,
CREATE MATERIALIZED VIEW,
AUTO CDC INTO,
expectations and
read_files,
Python API,
materialized_view,
Python expectations,
SQL Server ingestion,
Salesforce ingestion
and Kafka ingestion.
pnpm install
pnpm validate # format, licences, lint, types, tests, README code and API reportRequires Node 22.13 or later and pnpm 10. How code is written here: CONTRIBUTING.md. Security: SECURITY.md.
Questions and feedback: hello@ingestron.io. Security reports: security@ingestron.io (see SECURITY.md).
User documentation: https://docs.ingestron.io.
Licensed under the Apache License 2.0; see NOTICE.