CapyDB/ docs
Guides

Preview databases

Disposable Postgres databases for testing, review apps, debugging, and other short-lived environments.

Modes

  • empty creates a fresh, blank preview database
  • clone creates a preview from the current state of the project database

Both expose the same direct and pooled connection pattern as the main project, with their own generated credentials. A clone never accepts production's passwords: every login it carries in from production - including logins still in a rotation grace window - stops logging in, and only the preview's own credential works. If the project has a runtime role, the preview gets its own app_user password, kept across resets, under connections.app. Previews created before 2026-09-30 keep the logins they were cloned with until they are reset or recreated.

Previews run with synchronous_commit = off at the database level: commits do not wait for the WAL flush, which makes seed scripts and migration test runs substantially faster. The database stays crash-consistent - in the unlikely event of a crash, a preview can lose its last few seconds of commits, never its integrity. Your production database is unaffected, and a session that needs the stronger guarantee can SET synchronous_commit = on at any time.

Lifecycle and TTL

Every preview carries a TTL: 24 hours by default, 168 hours (7 days) maximum. When the TTL expires the worker deletes the preview - that is the backstop that keeps forgotten review databases from accumulating.

You can extend a running preview's TTL, reset it (re-clone or re-empty), or delete it early. Create, reset, restore-into, and delete are all tracked as separate jobs from primary project jobs.

The number of concurrent previews per project is plan-dependent: 1 on Vibe, 5 on Ship, unlimited on Business.

From the dashboard

The Previews tab on a project lists previews with state, mode, TTL expiry, and connection details, plus create/reset/extend/delete actions.

Each ready preview also has a SQL and lint button (the terminal icon) that opens two panels under it:

  • SQL on this preview - the Studio editor and result grid pointed at the preview, for rehearsing a statement before it touches the project. A statement that looks read-only runs at once, in a read-only transaction. Anything else asks This statement changes data on the preview. Run it? first, and then runs with the destructive-statement check lifted - so an unscoped DELETE can be tried here. Results show up to 500 rows. Runs are not recorded in the project's SQL history.
  • Schema lint - the preview's lint report, with the fix statement for each finding.

The button is disabled until the preview is ready.

From the CLI

capydb preview create --mode clone --name pr-42 --ttl-hours 72 --wait
capydb preview list
capydb preview reset --preview pr-42 --wait
capydb preview extend <preview-id> --ttl-hours 48
capydb preview delete --preview pr-42 --wait

Then connect to it directly:

capydb psql --preview <preview-id>
capydb connection-string --preview <preview-id> --pooled

Run SQL against a preview

POST /v1/preview-databases/{previewID}/sql (scope projects:write) runs one statement on a preview with the same guards as the project's SQL runner: a 15 second timeout, a row cap (default 200, maximum 1000), read_only for a transaction in which Postgres itself refuses writes, and the destructive-statement check. The preview must be ready (or failed).

curl -X POST https://capydb.dev/api/capydb/v1/preview-databases/$PREVIEW_ID/sql \
  -H "Authorization: Bearer $CAPYDB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "DELETE FROM sessions", "allow_unqualified_writes": true}'

The destructive-statement check refuses an UPDATE or DELETE with no WHERE (including one inside a WITH), a MERGE that touches rows its join does not scope, and TRUNCATE. Setting allow_unqualified_writes lifts it - which is what a preview is for: rehearse the destructive statement on a copy, check the result, then run the scoped version against production. Statements run on a preview are not written to the project's SQL history, which stays production's. A suspended organization's previews run read-only.

Lint a preview

GET /v1/preview-databases/{previewID}/lint returns the same report as the project's schema lint, so a pull request's migration can be checked for missing primary keys or unindexed foreign keys before it reaches production. Previews skip the unused- and redundant-index checks, which need a week of the project's statistics.

From CI

The GitHub Action creates an idempotent preview per pull request and exports DATABASE_URL / DATABASE_URL_UNPOOLED into the job. The Vercel and Netlify integrations create a preview per git branch automatically.

Restoring into previews

Previews double as the safe restore target: a restore can land in a brand-new preview or overwrite an existing one, which is how you inspect a backup or a point-in-time state without touching production.

Promoting a preview

When a preview is the state you want in production - a rehearsed migration, a restructured dataset - that is a cutover: an operator-assisted promotion with a verified pre-cutover backup taken first.

Webhook events

preview.ready and preview.deleted fire as webhook events when previews come and go.