CapyDB/ docs
Guides

Version upgrades

Which PostgreSQL majors CapyDB offers and on which channel, and how minor and major upgrades work - what is automatic, what you control, and what we apply on your behalf.

Versions and channels

A database's Postgres major is chosen when it is created and stays fixed for its life; previews, restores and branches inherit it. Each major is offered on a channel:

MajorChannelDefaultProduction ready
16previousYes
17stableYesYes
18currentYes
19betaNo - only while the beta is offered

GET /v1/postgres-versions (scope projects:read) lists the majors a new database can be created on right now, oldest first:

curl -s -H "X-API-Key: $CAPYDB_API_KEY" \
  https://capydb.dev/api/capydb/v1/postgres-versions
{
  "versions": [
    { "version": "16", "channel": "previous", "default": false, "production_ready": true },
    { "version": "17", "channel": "stable",   "default": true,  "production_ready": true },
    { "version": "18", "channel": "current",  "default": false, "production_ready": true }
  ]
}

Pass version as postgres_version when you create a project or an ephemeral database; omit it to get the default. Projects and ephemeral databases report the channel of their major as postgres_channel.

A create for a major that no node in the region offers yet is refused with a message saying so, rather than "no capacity".

The 19 beta

Postgres 19 is being rolled out as a beta channel for trying the next release early. While it is not offered, 19 is missing from GET /v1/postgres-versions and a create that asks for it is refused as not available for new databases yet. When it is offered it appears with channel: "beta" and production_ready: false, and every beta database carries a postgres_warning on its project (and ephemeral database) payload from the create response onwards. Show that warning wherever you show the database.

A beta database is not for production data:

  • No uptime or durability commitment.
  • Extensions may be missing. Only extensions already built for the beta are installed; enabling a missing one fails.
  • Backups and point-in-time restore are best effort. They stay on as for any database on your plan, but restores of a beta database are attempted, not promised.
  • No guaranteed way forward. A later beta or the final release may require recreating the database - a new database on the released major, with the data copied across - instead of upgrading it.

Nothing stops you marking a beta database production; the warning stays.

Minor and major upgrades

PostgreSQL versions move in two very different ways, and CapyDB treats them differently because the risk is not comparable.

Minor (17.4 → 17.5)Major (17 → 18)
On-disk formatIdenticalChanges
What it takesA restartA migration
Write interruptionSecondsFull copy and verification window
Who starts itMostly automaticYou check; CapyDB executes - or you, where self-serve upgrades are enabled
ReversibleNothing to reverseYes, with post-cutover writes discarded

Minor upgrades

Minor releases are binary-compatible. Your data is not touched, nothing is rewritten, and no migration runs - the new version is simply a different server binary reading the same files. Security fixes ship as minors, so this is the path that matters most.

If your database uses scale-to-zero, it upgrades itself. A paused database starts on whatever version is current when it next resumes, so most cells pick up a minor with no action and no interruption at all.

A database that is running when a minor lands keeps its current version until it restarts. The dashboard reports this, and you can apply it whenever suits you:

POST /v1/projects/{projectID}/upgrade/minor

The restart interrupts open connections briefly - that is the entire cost, and it is why we do not do it silently underneath you. The platform refuses to restart while queries are actually running, so a busy database is skipped rather than disrupted.

When we apply one on your behalf

For an urgent security release, CapyDB will apply a minor upgrade fleet-wide after a stated deadline, whether or not it has been applied by then. You are told in advance and can apply it earlier at a time you choose; the deadline exists so an unpatched database cannot sit exposed indefinitely. This is the one case where the timing is not entirely yours.

Major upgrades

Major upgrades change the on-disk format, so they are a real migration and never happen without you: you run the readiness check yourself, and the migration itself is performed with CapyDB when you are ready - or by your organization's admins directly, where self-serve upgrades are enabled.

Check first

A major upgrade can fail after it starts building the replacement, so known blockers are checked up front:

POST /v1/projects/{projectID}/upgrade/major/preflight?target_major=18

The result is upgradable or blocked, with specific reasons. The blocker that matters most in practice is extensions: if something you use has no build for the target major, migrating would leave your schema referencing types and functions that no longer exist. Better to know before than after.

The check changes nothing and can be re-run as often as you like. The target must be a major offered for new databases (see Versions and channels) and newer than the current one.

How the migration runs

CapyDB does not upgrade your database in place. It builds a new database on the target version. Immediately before the logical copy begins, the original is made read-only and existing client sessions are disconnected. It stays read-only while CapyDB copies and verifies the data, then your project is pointed at the verified replacement. Plan a maintenance window that covers the full copy and verification, not only the final switch.

The frozen original is retained until you confirm the upgrade or the retention window expires. Rollback points the project back at that copy, so every write accepted by the upgraded database after cutover is discarded. Stop writers before requesting rollback and preserve any post-cutover data you need separately. Confirmation destroys the retained copy and permanently removes this rollback path.

Your connection string does not change.

Running a major upgrade yourself

Self-serve major upgrades are rolling out. Until they are enabled for the platform, the upgrade, confirm and rollback endpoints answer 403 to organization callers, and a major upgrade is run with CapyDB as above.

Once enabled, an organization admin (from a dashboard session) or an organization-wide API key with projects:write and manager rights (manager: true - every key an admin creates; a key minted when a member without the admin role connects an MCP client has none) runs each step directly. Each step is destructive in its own way, so each needs its own single-use approval token minted by a person:

StepEndpointApproval action
UpgradePOST /v1/projects/{projectID}/upgrade/major?target_major=18&approval_token=…project.upgrade_major
Confirm (destroy the retained database)POST /v1/projects/{projectID}/upgrade/major/confirm?approval_token=…project.upgrade_major_confirm
Roll back (discard post-cutover writes)POST /v1/projects/{projectID}/upgrade/major/rollback?approval_token=…project.upgrade_major_rollback

An organization admin mints the token from a signed-in dashboard session with POST /v1/projects/{projectID}/approvals and the action in the body. API keys - including the one an agent received - get 403 there, so an agent cannot approve its own upgrade. The token is valid for 10 minutes, works once, and can be handed to any caller of the organization (the CLI, Terraform, CI) to present with its own credential. Presenting a token that already started a step returns the job it started instead of starting another. This is the same approval flow as overwrite restores.

From the CLI (v2.1.0 and later), with each token passed as --approval-token or CAPYDB_APPROVAL_TOKEN:

capydb upgrade major --target-major 18 --approval-token <token>   # runs the preflight first
capydb upgrade status                                              # state and rollback_available_until
capydb upgrade confirm --approval-token <token>                    # or: capydb upgrade rollback

The upgrade itself is refused with 400, naming the unmet condition, unless:

  • the database is a claimed project, not an unclaimed ephemeral database,
  • the organization is in good billing standing,
  • the target major is offered for new databases and newer than the current one (so 19 only while the beta is offered),
  • a preflight for that target on this database returned upgradable within the last hour.

Each step is recorded in the audit log with the actor.

From the dashboard

The project's Settings → General page has a Database version section:

  • Check a major version upgrade runs the preflight for the target major. Anyone in the workspace can run it; the picker appears when more than one newer major is offered, preselects the newest production-ready one, and marks a beta as such.
  • Upgrade to PG N is shown to workspace admins. It says the upgrade needs a passing check for that target from the last hour, shows the beta warning for a beta target, and asks for a confirmation before it starts. The dashboard creates the approval for its own call, so there is no token to handle. While self-serve upgrades are not enabled, starting one says so instead.
  • While an upgrade is in flight, the section shows its state. Once the project runs on the new major, it counts down the rollback window and offers Confirm and Roll back to admins, each behind its own confirmation.

For the CLI, Terraform or an MCP agent, the same section has an Approve from the CLI, Terraform or an agent panel per step - Create approval mints the single-use token and shows the command it goes into:

Panel shownApproval actionCommand
Before an upgrade, when a newer major is offeredproject.upgrade_majorcapydb upgrade major --target-major <major> --approval-token <token>
During the rollback windowproject.upgrade_major_confirmcapydb upgrade confirm --approval-token <token>
During the rollback windowproject.upgrade_major_rollbackcapydb upgrade rollback --approval-token <token>

The panels are shown to workspace admins only, since only an admin's session can mint approvals.

Status, confirm and rollback

GET /v1/projects/{projectID}/upgrade/major

returns the upgrade in flight, or upgrade: null:

stateMeaning
stagingThe new database is being built, copied and verified; the current one is write-fenced
rollback_availableThe project runs on the new major; the previous database is retained
confirming / rolling_backA confirm or rollback is running

From the cutover, rollback_available_until is 72 hours away. Until then you can confirm (destroying the retained database) or roll back. Once it passes, CapyDB confirms the upgrade on its own and confirm and rollback requests are refused.

Rolling back points the project at the retained database, which is the state at cutover, not a replica: every write committed after the cutover is discarded. Delete the project's preview databases before a rollback - it is refused while any exist.

What travels with your data

Every login on the database moves across, not only the owner: logins still inside a rotation grace window and the runtime role keep their passwords, expiry and memberships, and so does a rollback in the other direction - a password rotated after the cutover is the one that works after rolling back. Table and function privileges are re-applied after the copy. Per-role settings other than role (for example ALTER ROLE ... SET statement_timeout) are not copied; set them again after the upgrade.

The copy carries your planner statistics, not just your rows. A restored database with no statistics plans badly until something analyses it - which on a database that spends most of its time paused can be a long wait - so the upgrade analyses the replacement before the cutover rather than leaving the first queries to pay for it.

Schemas move across majors as written. That is why a column default calling a function whose name differs between majors is the one thing most likely to bite you here - see Postgres 18 features for the portable spelling.

Extensions

Extensions version independently of PostgreSQL. When a newer build of one you use becomes available, the dashboard offers an Update action on the Extensions page:

POST /v1/projects/{projectID}/extensions/{name}/update

Updates within an extension's major version are applied for you during routine platform maintenance: a patch or minor release such as 1.4.3 to 1.4.7 or 3.5 to 3.6, and for an extension still at 0.x, a patch such as 0.8.0 to 0.8.1. A database that is paused picks the update up when it next resumes. An update that fails leaves the extension on the version it had.

A new major version - 2.x to 3.0, or 0.7 to 0.8 - is yours to trigger with the action above, never applied automatically, because an extension's upgrade scripts can change behaviour inside your data - a PostGIS upgrade may require reindexing afterwards, for example. Until a same-major update has been applied for you, the action is offered for it too, and you can apply it early. Extensions CapyDB installs for its own observability are kept current automatically and are never offered here.

Applying an update when the extension is already current is a no-op, so it is safe to retry.