sluice

Migrating from Vultr Managed PostgreSQL with sluice

The only provider validated so far that ships CDC-ready with zero preparation: wal_level=logical and a REPLICATION-bearing admin role out of the box. Plus the pghoard_local platform-internal slot you must not drop, and Vultr's managed PgBouncer — the live counter-example where CDC actually traverses the pooler.

Vultr Managed Databases for PostgreSQL works with sluice's vanilla postgres engine — live-validated 2026-07-16 (PG 17.10) as a migration and slot-based CDC source: byte-identical bulk migrate on md5 ground truth (NaN in numeric[], ±Infinity, denormal floats, 2-D arrays with NULL elements) and exact CDC convergence with a clean snapshot → CDC handoff.

Enabling logical replication: nothing to do #

Vultr (an Aiven-lineage platform) ships CDC-ready — the only provider validated so far where that is true. wal_level=logical is set out of the box, and the master user (vultradmin) carries the REPLICATION attribute from first boot, so sync start works with zero preparation. max_replication_slots / max_wal_senders default to 20/20 and are raisable to 64 via the database's advanced options. For a custom role, ALTER ROLE <role> WITH REPLICATION works as vultradmin (no superuser needed — the platform patches the grant, like Cloud SQL and Azure).

sluice sync start \
    --source-driver postgres \
    --source 'postgres://vultradmin:[email protected]:16751/defaultdb?sslmode=require' \
    --target-driver postgres --target 'postgres://user:pass@target-host:5432/app?sslmode=require' \
    --stream-id vultr-pg-app

The pghoard_local slot is platform-internal #

Every Vultr PG instance carries an always-active physical replication slot named pghoard_local (Aiven's pghoard backup daemon). sluice knows it: sluice slot list shows it labeled platform-internal, sluice slot drop refuses it without --force, and the slot-health probe (scoped to sluice's own slot) never flags it. Leave it alone — never drop it, and don't count it as a leaked consumer. It's the Aiven-lineage sibling of Neon's wal_proposer_slot.

TLS #

Plaintext is refused server-side; sslmode=require works out of the box, and sslmode=verify-full works with the project CA, which is delivered inline in the database create/get API response (ca_certificate field) — save it and pass it via ?sslrootcert=<path> on the DSN. System roots do not verify (private per-project CA).

The connection pooler: CDC actually traverses it #

Vultr's managed PgBouncer pools listen on the primary hostname at port + 1 with dbname=<poolname> (the API does not expose this — it's the platform convention). Unlike the Neon / Supavisor / RDS-Proxy pooler class, replication connections pass through (modern PgBouncer ≥ 1.24 forwards them 1:1 to the server): both bulk migrate (parallel, snapshot-pinned, no statement-cache trip) and slot-based CDC — slot creation, streaming, warm resume, clean stop — were validated end-to-end through a transaction-mode pool. This is the live counter-example that makes the “a pooler always strips replication” claim provider-dependent.

Prefer the direct port anyway: a pool sized N permanently holds N of the plan's small connection budget (22 on the cheapest plan). Note that sluice's pooler-host WARN does not fire here — the pool hostname equals the primary's, only the port differs — which on Vultr is harmless, but it also means “no WARN” is not evidence of “not a pooler” on this provider.

Decommissioning #

A cleanly stopped sluice stream leaves its (resumable) replication slot in place; when done for good, sluice slot drop --yes <slot> — an abandoned slot retains WAL against the instance disk. (pghoard_local stays; see above.)

What sluice checks for you #

Next steps #