Near-zero-downtime migration (follow)
Stream a live source database into CapyDB and cut over with seconds of downtime instead of a full write freeze.
A plain import is point-in-time: you stop writers, dump, restore, and switch. For a busy production database that means a maintenance window as long as the copy takes. A follow import removes almost all of it — CapyDB base-copies the source, then streams the source's changes continuously until you cut over, so the only downtime is the few seconds of the cutover itself.
Under the hood a managed pgcopydb clone --follow does the initial copy and then applies the source's
logical-replication stream into your project's database. Your database cell has no outbound network by design,
so the follower runs on the CapyDB node, outside your cell.
Requirements
- Logical replication on the source. The source must run
wal_level=logical. Neon: enable Logical Replication in project settings. Supabase: on by default. Amazon RDS/Aurora: setrds.logical_replication=1and reboot. The start step re-checks this and fails fast with a clear message if it is not enabled. - A direct (or session-mode) source endpoint. Transaction-pooler URLs (Neon
-poolerhosts, Supabase port6543) are rejected — logical replication cannot run through them. - A fresh target project. Follow imports seed the project's database; run one into a new/empty project.
The flow
# 1. Start: base-copy + begin streaming. Returns immediately; the stream runs in the background.
# The follower writes into the project's live database from the start, so the start is
# confirm-gated like a plain import (interactive prompt, or --confirm for CI).
capydb import --follow --source-url "postgres://user:pass@direct-host:5432/db" --project my-app --confirm
# 2. Watch progress until replication lag is small.
capydb import follow-status --project my-app --wait
# 3. Stop writers on the SOURCE, then cut over. Drains the last changes, transfers ownership,
# drops the source replication slot, and flips the project live.
capydb import cutover --project my-app --waitTo cancel before cutover (removes the source replication slot cleanly):
capydb import follow-abort --project my-appThe same steps exist on the API (POST /v1/projects/{id}/imports/follow, .../follow/status,
.../follow/cutover, .../follow/abort) and in the SDK.
What cutover does (and the one rule)
Cutover marks the source's current LSN as the end position, waits for the follower to apply the stream up to it, transfers ownership of the migrated objects to your project role, drops the replication slot on the source, and marks the project ready. Sequences keep their positions, so there are no primary-key collisions.
The one rule: stop writes on the source before you cut over. Anything written to the source after the
cutover LSN is not carried over. In practice: put the app in read-only or point it at CapyDB, confirm
follow-status shows near-zero lag, then run cutover.
Provider notes
- Supabase: platform-managed schemas (
auth,storage,realtime, …) are excluded from the stream automatically, exactly as for a plain Supabase import; only your application schemas are copied. Foreign keys intoauth.usersmust be dropped or re-pointed first. - Neon: use the direct (non-
-pooler) endpoint and enable Logical Replication on the project first.
When to use plain import instead
If the source is small, or you can tolerate a short maintenance window, a plain capydb import is simpler —
no logical-replication prerequisite and one command. Reach for --follow when the write freeze of a
full-size copy is the problem you are trying to avoid.