Add `pgstream check` command for pre-migration sanity checks
@kvch is already working on this.
Since Jun 19, 2026.
- Dominant language
- Go
- Stars
- 1.2k
- Forks
- 82
- Avg merge
- 18h 22m
- Merged PRs (30d)
- 51
Description
Problem
A pgstream migration can fail many hours in:
- a
REPLICA IDENTITYmisconfiguration that only shows up the first time an UPDATE flows - an extension missing on the target that pg_restore only notices at COPY time
- the replication role missing the
REPLICATIONattribute max_replication_slotsalready exhausted- a target Postgres version that can't accept some syntax the source uses
- a column type pgstream can't decode buried inside an otherwise-clean schema
max_connectionstoo low for the configured worker count
Users who've already spent 10+ hours waiting for a snapshot or replication catch-up shouldn't be discovering setup issues at hour 10 that could have been flagged in 30 seconds before anything ran.
Requirements
- A single CLI invocation runs every applicable check using the same configuration source as
runorsnapshot. - Each finding has severity (error / warning / info) and an actionable message.
- Inapplicable checks (e.g. CREATEROLE when
roles_snapshot_mode: disabled) are skipped with a one-line reason. - Exit codes:
0clean,1error-tier findings,2validator failure.--strictupgrades warnings to errors. - Output supports human-readable and
--jsonformats.
Solution
Add a pre-migration check (working name: pgstream check) that runs every check that can be answered statically (connectivity, privileges, version, replication setup, schema compatibility, resource capacity) and returns a structured report.
The command reads the same YAML/env/flag configuration that pgstream run and pgstream snapshot consume, so users can validate the exact config they're about to migrate with.
Exit codes mirror validate rules: 0 clean, 1 blocking findings, 2 validator itself failed. --json produces a machine-readable report; default is human-readable with severity-tagged sections.
Checks to run
Connectivity & identity @kvch
- Source is reachable
- Target is reachable
- Source and target are not the same host/database (catches accidental same-DB config)
- Source and target Postgres versions are compatible (target >= source; flag known cross-version pitfalls)
Replication setup @kvch
-
wal_level = logicalon source -
wal2jsonis installed and loadable on source - At least one replication slot is available (
pg_replication_slotsvsmax_replication_slots) - Source role has the
REPLICATIONattribute - Per-table
REPLICA IDENTITYis sufficient for UPDATE/DELETE replication (default + PK, or FULL, or explicit USING INDEX) - Tables without a primary key or usable unique constraint are listed up front
- List tables that won't be replicated and why (foreign tables, materialized views, unlogged tables, partitioned parents without leaves, etc.)
Access & privileges @lghuy05
- Source role has
SELECTon all in-scope tables - Source role can read all in-scope sequences
- If
create_target_db: true: target admin role hasCREATEDB - If
roles_snapshot_modeisenabledorno_passwords: target admin role hasCREATEROLE - If
no_owner: false: source role has the rights needed to preserve ownership on the target (target role exists or can be created)
Schema compatibility @kvch
- All in-scope column types can be decoded by pgstream (mapper coverage)
- Extensions used by in-scope columns are installed on the source
- Extensions used by in-scope columns are installed (or installable) on the target
- All sequences referenced by in-scope tables exist on the source and are reachable
Resources
- Source can serve
snapshot_workers × table_workersconcurrent connections without exceedingmax_connections(account for existing usage, superuser slots reserved bysuperuser_reserved_connections)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.