Migrate: the safe lane
Migrate is the one command that can move real data — start on the safe lane: dry-run, review, then run.
# 1. Safe lane: plan-only dry-run — zero target writes
pgdba migrate --source postgresql://u@h:5432/a --target postgresql://u@h:5432/b \
--plan-only --state-dir /var/lib/pgdba-mig
# 2. Review migrate-report.json/.md: preflight refusals, table plan, warnings
# (each refusal carries its remediation text)
# 3. Real run — writes only after preflight passes and you confirm
pgdba migrate --source ... --target ... --state-dir /var/lib/pgdba-mig
# Non-interactive (CI) + parallel jobs + retry only failed tables
pgdba migrate --source ... --target ... --yes --jobs 8
pgdba migrate --source ... --target ... --retry-tables public.orders,public.events
What each phase refuses, and why
- Preflight — version/encoding/locale match, extension availability, disk space, privileges, 100GB scope cap. Mismatches abort with remediation text.
- Confirm — the table plan is printed and confirmed interactively;
--yesoverrides for CI; a non-TTY run without--yesaborts with zero target writes. - Copy — pgcopydb
clone --resumewhen available, otherwise phasedpre-data → per-table data → post-datawith per-table resume. - Verify — exact row counts, sequence vs
max(pk), invalid-index scan, targetANALYZE; non-zero exit with a diff table on mismatch.
The selected engine and any fallback are recorded in the plan and
migrate-report.json.
Danger
--force proceeds past scope/health gates with warnings recorded — only
when you accept the reported risks: it can migrate into a target preflight
flagged as incompatible or over the 100GB scope cap, and the warnings (not
refusals) land in migrate-report.json.
Warning
--yes skips the confirm prompt for CI — every action still passes
preflight gates, but no human sees the table plan before data moves.
Note
Runtime gating is deliberately not enforced (no refusal for --force
without a prior plan-only report): the safe lane is signposted here and in
pgdba migrate --help, not policed.
Done looks like
Exit 0 with verify.success: true and matching row counts in
migrate-report.json — or an early refusal with the exact remediation
instead of a half-copied target. Measured end-to-end at ~48 MB/s on the
evidence page.