Preview databases
Disposable Postgres databases for testing, review apps, debugging, and other short-lived environments.
Modes
emptycreates a fresh, blank preview databaseclonecreates 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
DELETEcan 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 --waitThen connect to it directly:
capydb psql --preview <preview-id>
capydb connection-string --preview <preview-id> --pooledRun 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.
SQLAlchemy
Connect SQLAlchemy 2.x to a CapyDB Postgres database: the postgresql+psycopg URL, an engine on the pooled endpoint, and Alembic migrations on the direct URL.
Ephemeral databases
A throwaway Postgres database with no account and no card. Run or test your app, then forget it existed - or claim it and keep building.