Projects, branches and point-in-time restore
kept in this browser
Edit this pageHow a Lakebase project is organised into copy-on-write branches, each with its own computes, roles and databases, and how expiry, protection, reset and point-in-time restore work on them.
On this page12
What it is
A project is the top-level container of Lakebase: one per application or team, created in the workspace’s region. Inside it, the unit you actually work with is the branch. A branch is a complete, isolated Postgres environment — its own data, its own computes, its own roles, databases and grants — that starts as a copy of another branch.
Every project begins with a root branch called production, which cannot be deleted, holding a database called databricks_postgres.
Why it exists
Anyone who has tested a schema migration against a copy of production knows the cost: dump, restore, wait, pay for a second server, and throw the copy away later if anyone remembers. With data in the tens of gigabytes the copy is already the slow part of the change.
Lakebase branches remove the copy. Because compute and storage are separate, a branch does not duplicate the data: it shares its parent’s storage and records only the pages that change afterwards (copy-on-write). Creating one takes the same time whether the database holds a megabyte or a terabyte. That makes a full copy of production cheap enough to create per pull request, per test run or per developer, and to throw away when it expires.
How it works
The shape of a project
project
└── branch: production (root)
├── computes one primary read-write, optional read replicas
├── roles Postgres roles
└── databases databricks_postgres, and any you create
└── branch: dev (child)
├── computes
├── roles
└── databases
A child starts with everything its parent had at that moment — data, roles, databases, grants — and from then on the two are independent. Writes on one are invisible to the other.
Creating a branch
A branch can start from the parent’s current data or from a past point in time within the project’s history window. From the CLI, a creation always states when the branch goes away:
databricks postgres create-branch projects/my-app dev \
--json '{"spec": {"source_branch": "projects/my-app/branches/production", "ttl": "604800s"}}'
The expiration is one of ttl (a duration), expire_time (a moment) or no_expiry. The maximum is 30 days from now, it can be extended, and when it arrives the branch is deleted, permanently. Expiry is not allowed on a protected branch, on the project’s default branch, or on a branch that has children.
Reset from parent
Reset replaces a child’s data with its parent’s latest state. It is a full overwrite in one direction only, parent to child: whatever was done on the child is lost. Connections drop for a moment, and the connection details stay the same, so an application reconnects to the refreshed data without being reconfigured. A root branch has no parent and cannot be reset.
The intended rhythm is: branch, change, test, reset, test again. Nothing flows back from child to parent; a schema change that passed on a branch is applied to production the same way it was applied to the branch, by running the migration there. The schema diff view helps check that, and compares DDL only, not data.
Protected branches
One branch per project can be protected, normally production. A protected branch cannot be deleted or reset, and it also blocks the deletion of its project and of its computes. It is never archived for inactivity and gets priority in the storage cache. When a child is created from it, roles on the child are given new passwords, so credentials that work on production do not work on a copy of it.
Point-in-time restore
Lakebase keeps a history of changes for every project, between 2 and 30 days, 7 by default. The window applies to the whole project and the history adds to storage.
A restore does not rewind the branch you are looking at. It creates a new root branch from the chosen moment, and leaves the original untouched. You inspect the restored copy, then either move the application to it or copy the rows you need back. A project can have at most three root branches, which caps how many restores can be kept side by side.
Limits worth knowing
| Limit | Value |
|---|---|
| Projects per workspace | 1,000 |
| Branches per project | 500 |
| Roles per branch | 500 |
| Databases per branch | 500 |
| Root branches per project | 3 |
| Protected branches per project | 1 |
| Active computes per project | 20, not counting the default branch |
| Manual snapshots | 10 |
A deleted project is soft-deleted and can be recovered for 7 days; deleting with --purge removes it at once.
Who may do what
Project permissions are CAN CREATE, CAN USE and CAN MANAGE, and by default workspace users have CAN CREATE. These govern the platform objects — projects, branches, computes. What a person can read inside a database is a separate question, answered by Postgres roles and grants on that branch (see Connecting to Lakebase, roles and permissions).
Example
A migration rehearsed on a short-lived branch:
# A copy of production for this change, gone in four hours
databricks postgres create-branch projects/shop pr-482 \
--json '{"spec": {"source_branch": "projects/shop/branches/production", "ttl": "14400s"}}'
# Run the migration against the branch's compute, then the test suite.
# Something wrong? Put the branch back to production's current data and try again:
databricks postgres reset-branch projects/shop/branches/pr-482
The same branch pattern fits CI: one branch per pipeline run, with a TTL of a few hours so a failed run never leaves a database behind.
Common mistakes
- Expecting merge. Branches do not merge back. Schema changes reach production by running the same migration there; data changes made on a branch stay on it.
- Resetting a branch with work on it. Reset is an overwrite, not a rebase. Anything written on the child since it was created or last reset is gone.
- Leaving branches without expiry. A forgotten
no_expirybranch keeps its compute, its storage and its history. Give development branches a TTL. - Restoring “in place”. Point-in-time restore creates a new root branch. The application keeps talking to the old one until you point it at the new one.
- Confusing the two permission layers. CAN USE on a project does not grant
SELECTon a table, and a Postgres superuser-like role does not let someone delete a branch.