ZEKTOR.IO Docs

Branching

Copy-on-write clones of a PostgreSQL database — a full, independent instance with your data, created in about a minute and deleted automatically when it expires.

Overview

A branch is a copy-on-write clone of a PostgreSQL database. It is a real, independent instance: its own host and port, its own plan, its own bill. What it is not is a second copy of your data — at the moment it is created it shares every block with the database it came from, and only what one side changes takes new space.

In practice that means branching a 9 MB database took 444 KB. A branch of a 50 GB database costs the same 50 GB of nothing, until you write to it.

Branches are meant to be disposable. A new branch deletes itself after 7 days unless you say otherwise.

Branching is a PostgreSQL feature. Valkey instances cannot be branched.

What you get

Behaviour
DataEverything the source had at the moment of branching — schema, rows, extensions
DivergenceIndependent from that moment on. Writes on either side never reach the other
Engine versionThe same PostgreSQL version as the source. A branch cannot be used to test an upgrade
RegionThe source's. A clone only works inside one node's storage, so there is no choice to make
RolesIts own default role, and nothing else — roles you added to the source are not carried over. See Connecting to a branch
BackupsNo backup schedule. A branch that lives a day does not need base backups; ask support if you want one

Can my database be branched?

Only databases whose data sits on branch-capable storage. If yours qualifies, the Branches tab appears in the sidebar.

If it does not, your database was created before that storage existed. Moving an existing database onto it is not supported yet — the honest options are to create a new database and migrate into it, or to wait. Either way, tell us; it helps us prioritise.

Every PostgreSQL database created from now on is branch-capable.

Creating a branch

From the dashboard

  1. Open the database and go to Branches
  2. Click New branch
  3. Give it a name — starts with a letter, then letters, digits, - or _, up to 63 characters
  4. Pick a plan. It defaults to the cheapest one, and that is usually right: a branch shares its storage with the source, so a small plan is a real choice rather than a compromise
  5. Pick Delete after — 1, 7, 30 or 90 days
  6. Create branch

It takes about a minute. The clone itself is instant; the time goes on starting a second PostgreSQL and waiting for it to report healthy.

From a coding agent

Branching is exposed through the MCP server, not the CLI:

create_branch       fork a database
list_branches       show a cluster's default and its branches
set_branch_expiry   extend the deadline, or remove it
delete_branch       throw one away
promote_branch      make a branch the default

delete_branch is available without ZEKTOR_MCP_ALLOW_DESTRUCTIVE — unlike delete_instance, which stays off unless you enable it. Forking, breaking and discarding is the whole point, and a branch is the one thing an agent is meant to be able to destroy. It still has to pass the branch's exact name as confirm_name.

When the node is full

A branch has to land on the same machine as the database it clones, because a clone only shares blocks inside one pool. If that machine has no room, the branch is refused immediately with a message saying so, rather than failing slowly. Contact us and we will add capacity.

Connecting to a branch

A branch is a separate instance with its own connection details — find them on the branch's own page, not the source's.

A branch gets its own default role, like any database. It starts there and nowhere else — the roles you created on the source do not come with it. Zektor never stores your PostgreSQL passwords in a readable form, so there is nothing to replay onto a branch.

To get a credential:

  • Dashboard — rotate the branch's default role, or add a new one, in its Roles section
  • Agents — create_ephemeral_role, which mints a login that expires by itself (60 seconds to 24 hours, one hour by default)

A password from the source may still work on the branch. A branch is a block-level copy, and PostgreSQL keeps its roles inside the data it copies — so the source's roles are physically present in the branch even though Zektor does not list or manage them there. They are frozen at the moment you branched: rotate a password on the source afterwards and the branch keeps the old one, because by then they are two separate databases. Delete the branch and they go with it.

See Connecting for connection strings and TLS.

Expiry

Every branch created through the dashboard or an agent gets a deadline. The Expires column shows what is left, and turns red inside the last day.

ActionEffect
Extend by 7 daysSets the expiry to seven days from now
Keep indefinitelyRemoves the expiry. The branch shows as kept and is never deleted automatically
Maximum90 days

An expired branch is deleted within the hour. Deletion is permanent and there is no undo — an expiring branch is not a backup, and nothing is archived on the way out.

Keeping a branch is a real decision, not a convenience. A branch pins the blocks its source had when it was created. As the source is rewritten, those old blocks cannot be freed while the branch holds them, and those old blocks count against the source's storage. A branch nobody looks at fills a database nobody touched, and if that database grows automatically, it grows — and never shrinks back. Delete it, or keep it deliberately.

Promoting a branch

Promote to default swaps which instance in the cluster is the default one. Nothing is deleted, nothing moves, and no connection string changes — the former default becomes a branch, and the branch becomes the default.

Neither side comes out of it with a deadline. The promoted branch loses its expiry, because it is your database now rather than a disposable copy of one, and the demoted default becomes a branch with no expiry at all. Nothing is deleted automatically after a promotion, in either direction — which also means the old default sits there costing its plan and its storage until you delete it.

The promoted branch is billed on its full size from then on, which is the source's size when it was branched, or larger if it was grown. A branch of a 50GB database promoted on AKPG-5 costs €5.00 + €6.75/mo.

Deleting

Delete a branch from its own page, like any instance. The source is untouched.

A database with live branches cannot be deleted — its storage is what those branches were cloned from. Delete the branches first, or promote one of them to default and then delete the old one.

Billing

A branch is billed on its plan's price, prorated by the hour it exists. Storage is shared with the source, so a branch adds no storage cost until it has written more than its plan includes (5GB on AKPG-5). What it writes above that is billed at €0.15/GB per month while it is a branch.

On AKPG-5 (€5.00/mo net):

Branch livesCost, net
An afternoon€0.03
A day€0.16
7 days — the default€1.15
A full month€5.00

Invoice lines name the branch, so a month's bill says where the money went:

Branch 'migration-test' - AKPG-5 (04.10 - 11.10)

Two details worth knowing:

  • A short-lived branch rounds to nothing. Invoice amounts are rounded to the cent, so anything under half a cent disappears. On AKPG-5 that is roughly the first 40 minutes; on AKPG-320 it is under a minute. Forking a database to check something and deleting it is, in practice, free.
  • A branch that fails to provision is never billed. Nothing was started, so nothing is charged.

See Pricing for VAT and the billing cycle.

What branches are good for

Use caseWhy a branch fits
Testing a migrationRun it against real data, at real size, before it touches production
Letting an agent looseGive a coding agent a branch instead of your database. The worst case is a wasted euro
Reproducing a bugFork production, poke at it with the safety off, throw it away
A staging environmentPromote when you are happy with it, or keep it and refresh it by branching again

For restoring from a backup or recovering to a point in time, use Backups & PITR instead. Those also produce a branch, but a different kind: a full restore from object storage with its own independent storage, no shared blocks and no expiry, billed on its full size, like any database.

Next Steps

On this page