Data Migration
The data paths for migrating a running Zalando cluster to CloudNativePG. Manifest conversion is covered in Manifest mapping.
TOC
Choosing a pathPath 1 — declarative import (operator-driven dump/restore)Path 2 — logical replication (minimal downtime)Path 3 — physical clone (pg_basebackup bootstrap)After any pathChoosing a path
WAL shipping from the Zalando archive is NOT a path. Spilo archives WAL with WAL-E/WAL-G in a layout barman-cloud cannot read; a CNPG cluster cannot recover from a Zalando S3 archive. Backup history does not carry over — take a fresh CNPG base backup immediately after cutover.
All paths need network reachability from the CNPG pods to the Zalando
master service (<zalando-cluster>.<ns>.svc:5432) and a sufficiently
privileged user on the source. Common preparation:
Path 1 — declarative import (operator-driven dump/restore)
CNPG runs pg_dump/pg_restore for you at bootstrap
(bootstrap.initdb.import): it connects to the source, dumps the
selected database(s) and roles, restores into the new cluster, and then
proceeds as a normal cluster. Recommended for small/medium databases —
and the only path that also upgrades the major version in the same
step.
type: monolithwithdatabases: ["*"]androles: ["*"]migrates everything in one shot.- Quiesce writes on the source first — the import is a point-in-time dump.
- Run
ANALYZEafterwards (statistics are not dumped).
Path 2 — logical replication (minimal downtime)
Initial copy plus continuous change streaming; cut over when lag
reaches ~0. The source must have wal_level=logical (Spilo default in
recent versions — verify with SHOW wal_level;).
-
Bootstrap the CNPG cluster schema-only (Path 1 import with
schemaOnly: true, or restorepg_dump --schema-onlymanually — logical replication does not copy DDL). -
Publication on the Zalando primary (database
orders): -
Subscription on the CNPG cluster (declarative):
-
Monitor
pg_stat_subscription(CNPG side) andpg_stat_replication(source side); wait for initial sync to finish and lag to stay ~0. -
Cut over: stop writes → wait for lag 0 → sync sequences (logical replication does not replicate them — set them from the source's
pg_sequencesvalues) → drop the Subscription → repoint applications. -
Take the first CNPG base backup; decommission the source.
Caveats: tables need a replica identity (primary key) for UPDATE/DELETE; DDL changes during the migration window are not replicated; large objects are not replicated.
Path 3 — physical clone (pg_basebackup bootstrap)
A byte-level copy streamed from the running Zalando primary — same
PostgreSQL major on both sides, all databases, no schema work
(equivalent of Zalando's live clone).
Requirements: the source must allow replication connections from the
CNPG pods (the Zalando standby user is suitable) with
max_wal_senders headroom.
The copy is point-in-time — writes after the basebackup are not
replicated; stop writes before starting. Spilo artifacts (the standby
role, Patroni helper objects) remain in the catalog afterwards —
harmless, remove at leisure.
For a long-running synchronized standby (replicate now, promote at
cutover), add replica: {enabled: true, source: zalando-src} on top of
the same pg_basebackup bootstrap — the cluster keeps streaming from
the Zalando primary until you flip replica.enabled to promote.
After any path
- First CNPG base backup (
Backupresource, barman-cloud plugin) — verifyphase: completed; configure theScheduledBackup. - Monitoring: PodMonitor + dashboards (the Grafana dashboards guide, How To section) — WAL segments awaiting archive should be 0.
ANALYZEon the new cluster (paths 1–2).- Validate application credentials, connection limits, and the Pooler.
- Keep the Zalando cluster paused (not deleted) during a rollback window.