**Short answer:** keep it small. Start from the four Databricks policy families (Personal, Shared, Power User, Job Compute) and add a custom policy only where limits or cost tags really differ. Then enforce three things everywhere: a cost cap per resource (`dbus_per_hour`), a required cost tag (`custom_tags.<tag>`), and `CAN_USE` only for the groups that need each policy. Manage them as code [1][2][3].
## 1. How many policies
In my client work it's usually the four defaults plus a handful of custom ones, roughly one per workload type. A policy per team only pays off when that team's limits or tags are different. Every extra policy is one more thing to keep compliant.
## 2. How to categorize them
| Workload | Start from family | Rules worth adding |
|---|---|---|
| Personal exploration | Personal Compute | low `dbus_per_hour` cap |
| Shared interactive / BI | Shared Compute | `autotermination_minutes` range, cost tag |
| ML / power users | Power User Compute | node-type allowlist, cost tag |
| Jobs / ETL | Job Compute | `dbus_per_hour` cap, cost tag |
| Lakeflow pipelines (classic) | custom | `cluster_type` fixed to `dlt` |
The Job Compute family already fixes `cluster_type` to `job`, so you only add overrides. Use a policy with `cluster_type` = `dlt` for classic pipeline compute [2][3].
## 3. Best practices
1. **Inherit, then override.** Create policies from a family and put only your changes in the overrides. I checked that the family rules and the overrides merge into one definition [2].
2. **Cap cost per resource.** `dbus_per_hour` supports only a `range` with `maxValue`. "Max compute resources per user" limits how many resources someone can create, but it doesn't terminate resources that already run over a lowered limit [1][3].
3. **Force tags.** `"custom_tags.team": {"type": "allowlist", "values": [...]}` makes cost attribution non-optional [3].
4. **Put libraries in the policy, not in init scripts** [1].
5. **Re-enforce after edits.** Existing compute isn't updated when you change a definition. Use **Enforce** (default: on next restart) or the compliance scan to find drift [1].
6. **Keep policies in Git.** `cluster_policies` is a bundle resource (direct engine) [4]:
```yaml
bundle:
name: compute-policies
engine: direct
resources:
cluster_policies:
etl_jobs:
name: etl-jobs
description: Job compute only, capped DBU/h, team tag required
policy_family_id: job-cluster
policy_family_definition_overrides: |
{
"dbus_per_hour": {"type": "range", "maxValue": 10},
"custom_tags.team": {"type": "allowlist", "values": ["data-eng", "analytics"]}
}
permissions:
- level: CAN_USE
group_name: data-engineers
```
7. **Cover serverless separately.** Compute policies shape classic compute. For serverless notebooks, jobs and pipelines, use serverless usage policies to attach cost-attribution tags [5].
## What I'd do
Grant the four defaults to the right groups, add one capped and tagged Job Compute policy for ETL and one for shared interactive work, deploy them from a bundle, and run a compliance scan after every definition change.
## References
[1] Create and manage compute policies | Databricks on AWS โ https://docs.databricks.com/aws/en/admin/clusters/policies
[2] Default policies and policy families | Databricks on AWS โ https://docs.databricks.com/aws/en/admin/clusters/policy-families
[3] Compute policy reference | Databricks on AWS โ https://docs.databricks.com/aws/en/admin/clusters/policy-definition
[4] Declarative Automation Bundles resources | Databricks on AWS โ https://docs.databricks.com/aws/en/dev-tools/bundles/resources
[5] Attribute usage with serverless usage policies | Databricks on AWS โ https://docs.databricks.com/aws/en/admin/usage/budget-policies