agentsclimarketplace

Postgres operator cloudnative pg migration

Skill air-gapped/skills/.claude/skills/postgres-operator-cloudnative-pg-migration

Claude Code plugin marketplace — 58 installable reference skills across vLLM/SGLang inference, Kubernetes & Harvester, GPU host bring-up, observability, security, and agent workflows.

Install
npx -y skills add air-gapped/skills --skill postgres-operator-cloudnative-pg-migration

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Migrate PostgreSQL clusters from the Zalando postgres-operator (acid.zalan.do `postgresql` CRs, Spilo/Patroni, WAL-G) to CloudNativePG (CNPG) on Kubernetes, incl. fully air-gapped clusters. Core knowledge: the two walls (Spilo↔CNPG glibc/collation divergence that corrupts physically-copied indexes; WAL-G↔Barman archive incompatibility that strands old backups), three migration paths (logical replication default; initdb.import for small DBs/PG≤13; pg_basebackup as the discouraged same-major exception), the acid.zalan.do→Cluster manifest field map with no-equivalent gaps (preparedDatabases, sidecars, logical-backup cron), backup re-plumbing onto the barman-cloud CNPG-I plugin, HA parity (synchronous + failoverQuorum vs Patroni failsafe), consumer cutover (service/secret renames, scram, cnpg_ metrics), air-gap mirroring, stay-vs-migrate evidence.

SKILL.md

10.9 KB, ~2.4k tokens by cl100k_base, as published. Nobody here has run it

postgres-operator-cloudnative-pg-migration

Migrate PostgreSQL clusters from the Zalando postgres-operator (Spilo + Patroni) to CloudNativePG with data intact, indexes trustworthy, backups restorable at every phase, and no dependency left on maintenance-mode artifacts. Facts below were verified 2026-07-24 against primary sources (both operator repos at HEAD, release manifests, maintainer statements); re-verify anything version-gated before relying on it in a later year. Version anchor at authoring time: Zalando 1.15.1 (Spilo-17 4.0-p3, PG 13–17), CNPG 1.30.0 / 1.29.2 (PG 14–18), plugin-barman-cloud v0.13.0.

Why this migration exists

Zalando's operator is in maintainer-confirmed maintenance mode ("a little idle state… not encouraged by management", issue #2921, June 2025): ~1 release/year, one sustained maintainer, no release in 2026 despite a commit rebound. CNPG runs ~7× the human commit volume, releases quarterly across three maintained lines, entered CNCF Sandbox (Jan 2025, incubation pending), and has a documented migration wave (IBM Instana ships official Zalando→CNPG docs). This is strategic, not an emergency: Zalando is safe to run through ~2027 (v1.15.x covers PG13–17 and the K8s-1.33 Endpoints deprecation), Patroni itself is thriving, and CNPG brings its own churn costs (quarterly operator upgrades that rolling-restart every cluster; a backup-plugin transition in flight). Full evidence, the skeptic's case, and alternatives: references/decision.md. Migrate deliberately, cluster by cluster.

The two walls that shape every plan

Wall 1 — glibc/collation (physical data paths). Spilo images are Ubuntu (glibc 2.35; or a bundled Ubuntu 18.04 archive at glibc 2.27 when USE_OLD_LOCALES=true — common on long-lived fleets). CNPG operand images are Debian bookworm/trixie (glibc 2.36/2.41). Zalando's initdb default is en_US.UTF-8 (libc), so collation-sensitive btree indexes are the norm. Any physical copy (pg_basebackup, replica promotion) crosses at least one glibc boundary — for USE_OLD_LOCALES clusters it crosses the catastrophic glibc-2.28 ISO-14651 break — and text indexes, unique constraints, and partition bounds become silently corrupt. REFRESH COLLATION VERSION only silences the warning. Logical paths (replication, import) rebuild indexes on the destination and dodge this entirely. Triage per cluster before choosing a path: SELECT datname, datcollate, datcollversion FROM pg_database; plus checking USE_OLD_LOCALES in the pod env.

Wall 2 — WAL-G ↔ Barman (backup archives). Current Spilo backs up with WAL-G; CNPG restores only Barman-Cloud-layout object stores. The formats are disjoint (basebackups_005/wal_005 vs base/wals) with no interop or conversion tool in either project. Consequences: a CNPG cluster cannot bootstrap or PITR from the Zalando S3 archive, and after cutover the old bucket is restorable only by a Spilo/WAL-G stack — freeze it read-only and keep the retired cluster (or a documented resurrection path) until the retention window expires. Take a fresh CNPG base backup immediately at cutover.

Choosing a migration path

Three paths; pick by PG major, size, downtime budget, and Wall 1 status. Full recipes with YAML and cutover sequences: references/migration-paths.md.

PathWhenDowntimeWall 1
A. Logical replication (Publication/Subscription CRDs, CNPG ≥1.25)default for production; source PG ≥10; no large objectsnear-zero (write pause at cutover)immune
B. initdb.import (pg_dump/pg_restore)small DBs; PG ≤13 sources (CNPG images are 14–18 — jump majors in one hop); large objectsfull copy windowimmune
C. pg_basebackup / replica promotesame major only; TB-scale where logical copy is impracticalshort (promote)exposed — amcheck + full REINDEX of text indexes mandatory

The CNPG maintainer explicitly discourages C from Zalando sources ("You should not use physical replication to migrate from Zalando", #5736) — yet IBM Instana's supported runbook is exactly C, with Spilo conf-path fixes. Both are real; default to A, and treat C as the constrained exception it is.

Path A skeleton: set wal_level: logical on the Spilo manifest (rolling restart) → CNPG Cluster with bootstrap.initdb.import + schemaOnly: true + externalClusters pointing at the Spilo primary (reuse the Zalando credentials secret) → kubectl cnpg publication create --external-cluster (runs SQL on the source) → subscription create → watch lag → pause writes → kubectl cnpg subscription sync-sequences → repoint apps → drop pub/sub. Not carried: DDL, sequence values (the sync-sequences step), large objects, matviews (REFRESH after).

Translating the manifest

No conversion tool exists anywhere — translation is manual against the field map in references/manifest-map.md (every acid.zalan.do field → CNPG equivalent or explicit gap, with minimum CNPG versions). The gaps that most often force design decisions: preparedDatabases role trios (reconstruct via managed.roles + inRoles + postInitApplicationSQLRefs), password auto-generation for extra users (CNPG requires pre-created basic-auth Secrets — #3788), arbitrary sidecars/initContainers (CNPG-I plugins only), enableLogicalBackup pg_dump cron (hand-build), and maintenanceWindows (nodeMaintenanceWindow is a false friend — it controls PVC reuse, not upgrade timing). Strip CNPG's ~60 operator-owned parameters from any copied postgresql.parameters map.

Extensions: Spilo bundles timescaledb/postgis/pg_cron/pg_stat_kcache/ set_user/…; CNPG standard images add only PGAudit, pgvector, pg_failover_slots, locales, JIT. pg_restore fails on missing extensions. Drop unused Spilo extensions pre-dump, use the official postgis image, or build a custom operand image (TimescaleDB has no official CNPG image; timescale/timescaledb-ha is incompatible).

HA parity

CNPG closed most of the Patroni gap: default-on primary isolation self-fencing (1.27), quorum-gated failover (1.28), primary lease (1.30). The honest residual: under partial partition (primary loses the API server but reaches ≥1 peer) CNPG never fences where Patroni's failsafe demotes — with async replication that is a real acknowledged-write split-brain window. For Patroni-equivalent durability, migrated production clusters get 3 instances + postgresql.synchronous: {method: any, number: 1, dataDurability: required, failoverQuorum: true}. Details, defaults, and open HA bugs to watch: references/pitfalls.md §HA.

Backups, cutover, air-gap

  • Backups: go plugin-first (in-tree barmanObjectStore removal is scheduled for CNPG 1.31 after slipping four times — never hard-code that). ObjectStore CRD wiring, the sidecar image hidden in the release manifest, cert-manager avoidance, dashboards for renamed barman_cloud_* metrics, and the frozen-WAL-G-bucket plan: references/backup-chain.md.
  • Consumer cutover: services <cluster>/<cluster>-repl<cluster>-rw/-ro/-r; secrets <user>.<cluster>.credentials…<cluster>-app/-superuser (richer keys); md5 password hashes surviving physical/monolith moves fail against scram-only pg_hba; pg_* exporter metrics → cnpg_* with no shim. Checklists: references/app-cutover.md.
  • Air-gap: the mirror list is operator + operand (-standard- flavor keeps en_US.UTF-8 available) + pgbouncer + plugin + sidecar images, two Helm charts; no telemetry to disable; cosign-verify at the mirror boundary. Registry knobs (PULL_SECRET_NAME, POSTGRES_IMAGE_NAME): references/airgap.md.

Side-by-side operation of both operators in one cluster is safe (different API groups; Zalando registers no webhooks) — but never reuse a Zalando cluster name in the same namespace: Zalando pods <name>-0,1… and CNPG pods <name>-1,2… collide at <name>-1.

Pitfalls quick index

Before executing any plan, scan references/pitfalls.md (severity- ordered). Headliners beyond the walls: CNPG initdb defaults to C locale (silent ORDER BY change vs Spilo's en_US.UTF-8 — set locale explicitly, or move to ICU/builtin to escape glibc); enableSuperuserAccess defaults false; CNPG pg_hba APPENDS where Zalando REPLACES; monolith import strips SUPERUSER; first plugin backup on an idle cluster is unrestorable until a WAL switch; SSL cannot be fully disabled in CNPG.

Reference files

FileRead when
references/decision.mddeciding stay-vs-migrate; trend evidence, skeptic's case, alternatives
references/migration-paths.mdexecuting a migration: full recipes for paths A/B/C, per-cluster pre-flight triage
references/manifest-map.mdtranslating an acid.zalan.do manifest; field map + gaps + operator-config knobs
references/backup-chain.mdre-plumbing backups onto the barman-cloud plugin; WAL-G retention plan; snapshot-only option
references/app-cutover.mdrepointing applications; secret/service/auth/monitoring checklists
references/airgap.mdair-gapped mirroring: exact images, charts, registry configuration
references/pitfalls.mdalways, before executing a migration plan
references/sources.mdverifying or freshening any dated claim

What ships with it: 10 files

71.5 KB alongside SKILL.md

evals/

Gives 0 of the 12 instructions most databases sql skills give in ~2.4k tokens

Counted across 589 of the 662 authors here whose files we hold, read 2026-08-07

  • Use parameterized queriesin 37 of 589, across 34 files
  • Use timestamptz for timestampsin 30 of 589, across 14 files
  • Index foreign keysin 29 of 589, across 18 files
  • Create indexes concurrentlyin 29 of 589, across 24 files
  • Use numeric type for moneyin 25 of 589, across 8 files
  • Use cursor pagination instead of offsetin 24 of 589, across 17 files
  • Select only required columnsin 24 of 589, across 20 files
  • Add indexes manually on foreign key columnsin 22 of 589, across 12 files
  • Normalize to third normal formin 19 of 589, across 10 files
  • Configure connection poolingin 19 of 589, across 17 files
  • Put equality columns before range columns in indexesin 18 of 589, across 10 files
  • Read individual rule files for detailed explanationsin 18 of 589, across 4 files

Said here and by no other author read

  • re-verify version-gated facts before relying on them
  • migrate deliberately cluster by cluster
  • check cluster locale and glibc version before choosing a path
  • default to logical replication for production migrations
  • take a fresh base backup immediately at cutover
  • freeze old backup buckets read-only until retention expires

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.