Databricks Guide

Unity Catalog structure: how to lay out catalogs, schemas, and grants

By the engineer who builds Datadef, from client work on data platforms · Reviewed August 21, 2026

Unity Catalog gives you a three-level namespace, catalog then schema then object, and almost no opinion about how to use it. The layout you pick on day one decides how easy it is to isolate environments, promote a table to production, grant access to a domain team, and explain the platform to somebody new. This page covers the three layouts that work, what each one costs, and how to keep a picture of the structure that matches reality.

8 min readFor platform teams standing up or reorganizing a Databricks workspace

See it as a diagram

Everything below, as a diagram you can edit. Describe yours and see it in seconds.

148/20003 credits left
Try:

No account needed · Editable canvas, not a picture

Where isolation actually happens

The metastore is regional and shared. It is the top of the hierarchy, but it is not the isolation boundary you should be designing around, because a single metastore is meant to serve many workspaces in the same region. Treating one metastore per team as the isolation strategy creates a sharing problem you will spend the next year working around.

The catalog is the primary unit of isolation, and that is not an opinion of ours: the Databricks Unity Catalog best practices page states it directly, describing catalogs as the primary unit of data isolation in a typical Databricks governance model. Grants, ownership, and default storage all attach cleanly at the catalog level, a catalog can be bound to specific workspaces so certain data is only processed there, and the three-level namespace keeps catalog.schema.table readable in every query. When somebody asks where the boundary is, the answer should be the catalog.

The schema is the unit of team ownership inside a boundary. The pattern that ages well is a schema per team or per domain, with USE SCHEMA and CREATE TABLE granted to that team and USE CATALOG on the parent. External tables are cleanest with one external location per schema, which keeps storage permissions aligned with the namespace instead of cutting across it.

Three layouts, and what each one costs

Environment first: catalogs named dev, staging, and prod, with medallion schemas inside each. This is the layout most teams end up with, because it makes the isolation that matters most, production versus everything else, the strongest boundary in the system. Promotion is a deep clone or a rerun of the pipeline against the production catalog. The cost is that domain ownership has to be expressed at the schema level, which gets crowded once you have twenty domains.

Domain first: one catalog per domain, with layer schemas inside and environments separated by workspace binding or by a suffix. This suits organizations that already work as data product teams, and it makes cross-domain sharing an explicit grant rather than an accident. The cost is that a mistake in production is now possible in twenty places instead of one, so grants need to be automated rather than clicked.

Medallion first: bronze, silver, and gold as catalogs, with domains as schemas inside them. This reads well on a slide and creates friction in practice, because a domain team then owns schemas in three different catalogs and the natural unit of ownership is split across boundaries. It works when the layers really do have different governance owners, which is rare.

Whichever you pick, assign catalog ownership to a group rather than an individual, which is also what the Databricks best practices page tells you to do for production catalogs and schemas. An owner who leaves the company is the most common way a catalog becomes unmanageable.

# catalogs/main.tf
resource "databricks_catalog" "env" {
  for_each     = toset(var.environments)   # ["dev", "qual", "prod"]
  metastore_id = var.metastore_id
  name         = each.key
  owner        = "platform-data-owners"
  comment      = "Isolation boundary for the ${each.key} environment"
}

resource "databricks_schema" "layers" {
  for_each     = local.env_layer_pairs      # dev/bronze, dev/silver, ...
  catalog_name = databricks_catalog.env[each.value.env].name
  name         = each.value.layer
  owner        = each.value.owner_group
}

resource "databricks_grants" "team_schema" {
  schema = "${databricks_catalog.env["prod"].name}.gold"
  grant {
    principal  = "finance-analytics"
    privileges = ["USE_SCHEMA", "SELECT"]
  }
}

Getting the real structure onto a diagram

Catalog layouts are hard to explain in words and trivial to explain in a picture, which is why most teams have a slide that was accurate once. If your catalogs, schemas, and grants are declared in Terraform, that repository is the current structure, and Datadef reads it directly. Connect the repository read-only from GitHub, GitLab, or Azure DevOps, pick a branch or tag, and every .tf and .tfvars file is parsed. No terraform init, no state file, no workspace credentials.

What lands on the canvas is curated rather than complete, and the curation is deterministic rather than left to a model. Datadef carries an explicit opinion per resource type: databricks_metastore and databricks_catalog are boxes the reader came to see, databricks_schema is supporting detail that nests inside its catalog through catalog_name, and databricks_grant, databricks_grants, databricks_permissions and databricks_metastore_assignment are wiring that never becomes a box at all. They are counted instead, so a stack summary reads like 41 wiring resources not worth boxes: 18 grants, 12 permissions, 6 role assignments. A permission graph drawn as arrows is the fastest way to make a catalog diagram unreadable, and this is the decision that prevents it.

Multiplicity comes out honest rather than tripled. A catalog resource created with for_each over environments stays one node stating the real counts read from the tfvars under environments/DEV and environments/PROD, such as 10 catalogs in DEV and 1 in PROD, instead of a copy of the platform per environment. Modules become zones, so a catalogs module and a grants module read as separate areas of the diagram.

The sync runs daily and on demand, regenerates both the diagram and an architecture.md, skips commits that change nothing structural, and preserves nodes you moved by hand. Put the result in the platform wiki with a one-line live embed so the picture updates without anyone exporting anything.

If the structure is not in Terraform

Describe the intended layout in plain language and generate it on the canvas instead, then keep editing it there or through an MCP client like Claude Code. The sync only reads repositories, so a hand-provisioned metastore is drawn, not discovered.

FAQ

How many catalogs should a Unity Catalog metastore have?

As many as you have real isolation boundaries, which for most organizations means one per environment, or one per domain if teams own their data products end to end. Catalogs are the primary unit of isolation for grants, ownership, and default storage, so the count should follow governance boundaries rather than table volume.

Should medallion layers be catalogs or schemas?

Schemas, in most layouts. Making bronze, silver, and gold into catalogs splits a domain team ownership across three isolation boundaries, which complicates grants without adding protection. Layers as schemas inside an environment or domain catalog keeps ownership in one place.

Can a query join tables across two catalogs?

Yes. The three-level namespace lets one query reference catalog_a.schema.table and catalog_b.schema.table, provided the principal holds USE CATALOG and USE SCHEMA on both plus SELECT on the tables involved. That is what makes catalog-per-domain workable rather than siloed, and it is why the count of catalogs should follow governance boundaries rather than a fear of splitting the data.

Who should own a catalog?

A group, never an individual. Ownership carries the ability to grant and to drop, so an individual owner becomes a single point of failure the day they change teams. A named governance group, for example platform-data-owners, keeps the permission chain intact through staff changes.

How do you keep a Unity Catalog diagram from going stale?

Generate it from the repository that declares the structure. When catalogs, schemas, and grants are defined in Terraform, a read-only repository connection can parse the source files on a daily schedule and regenerate the diagram and its architecture document, without touching state or the metastore itself.