#Manifest Mapping
Field-by-field mapping from the Zalando CR (acid.zalan.do/v1 postgresql) to CloudNativePG resources (postgresql.cnpg.io/v1).
An automated conversion script, zalando-to-cnpg, ships with the
product delivery (operator source repository, hack/zalando-to-cnpg/):
it emits a Cluster plus Pooler/Database resources and prints an
explicit WARN for every field it cannot map — treat each warning as a
checklist item.
Legend: direct = copy; transform = rename/convert; mechanism = different resource or approach; none = no equivalent (the tool warns).
#TOC
CoreStorage and resourcesUsers and databasesPostgreSQL configurationPooling, services, networkingScheduling and pod attributesBackup, clone, standby, TLS, CDCFork-specific Zalando fields (previous ACP PostgreSQL releases)Worked example#Core
| Zalando | CloudNativePG | Kind | Notes |
|---|---|---|---|
metadata.name | metadata.name | direct | No teamId- prefix rule in CNPG; renaming changes service DNS names. |
spec.teamId | — | none | Convention only; preserved as a label via inheritedMetadata. |
spec.numberOfInstances | spec.instances | direct | Zalando 0 (hibernation) → annotation cnpg.io/hibernation: "on", not instances: 0. |
spec.dockerImage | spec.imageName | transform | Never copy — Spilo images are unusable with CNPG. Derive from postgresql.version → PostgreSQL operand image for that major. |
spec.postgresql.version | image major | transform | CNPG infers the version from the image. |
spec.env | spec.env | transform | Drop Spilo/WAL-G variables (WAL_S3_BUCKET, USE_WALG_*, AWS_*, …) — backup config moves to the barman-cloud ObjectStore. |
#Storage and resources
| Zalando | CloudNativePG | Kind | Notes |
|---|---|---|---|
spec.volume.size | spec.storage.size | direct | In-place growth supported; no shrink (either side). |
spec.volume.storageClass | spec.storage.storageClass | direct | |
spec.volume.selector | spec.storage.pvcTemplate.selector | transform | |
spec.volume.subPath / iops / throughput | — | none | Fixed PGDATA layout; IOPS/throughput were AWS EBS API calls — express via the StorageClass. |
spec.additionalVolumes | — | none (partial) | Secrets/ConfigMaps → spec.projectedVolumeTemplate; data volumes → spec.tablespaces / spec.walStorage. |
spec.resources | spec.resources | direct | If absent in the Zalando CR, operator-config defaults applied — set explicit values in CNPG. |
#Users and databases
| Zalando | CloudNativePG | Kind | Notes |
|---|---|---|---|
spec.users | spec.managed.roles[] | transform | Flags map 1:1
(superuser, createdb, createrole, replication, bypassrls; nologin → login: false). ⚠ Zalando users are LOGIN by default; CNPG managed roles default login: false — the tool always emits login explicitly. |
| credential secrets | managed.roles[].passwordSecret | mechanism | CNPG expects kubernetes.io/basic-auth secrets. Copy passwords from the Zalando <user>.<cluster>.credentials... secrets to keep application credentials working. |
spec.usersWithSecretRotation (+ in-place variant) | — | none | No automated password rotation. |
spec.databases | bootstrap.initdb.{database,owner} + Database CRs | transform | First entry bootstraps the cluster; each additional one becomes a Database resource. |
spec.preparedDatabases | Database CR (schemas, extensions) + roles | transform | The Zalando auto role hierarchy (<db>_owner/_reader/_writer + default privileges) is not auto-created — synthesize roles and GRANTs (e.g. postInitApplicationSQL). |
#PostgreSQL configuration
| Zalando | CloudNativePG | Kind | Notes |
|---|---|---|---|
spec.postgresql.parameters | spec.postgresql.parameters | transform | Copy, minus operator-controlled parameters (the tool strips and warns): archive_*, restore_command, port, hot_standby, recovery targets, ssl*, synchronous_standby_names, shared_preload_libraries (→ dedicated field), listen/log/file-path settings. Tuning parameters (max_connections, shared_buffers, …) all pass through. |
spec.patroni.pg_hba | spec.postgresql.pg_hba | transform | CNPG prepends fixed rules and appends host all all all scram-sha-256; Zalando's list replaced the whole file. Spilo-specific lines (pam, standby) are stripped. |
spec.patroni.initdb | spec.bootstrap.initdb | transform | encoding/locale/data-checksums map; only relevant for fresh bootstrap. |
spec.patroni.synchronous_mode | spec.postgresql.synchronous | transform | → {method: any, number: <synchronous_node_count or 1>}; non-strict → dataDurability: preferred, synchronous_mode_strict: true → required. |
spec.patroni.slots | spec.replicationSlots | mechanism | HA slots are automatic; permanent logical slots for CDC → Publication/Subscription resources or SQL. |
spec.patroni.ttl / loop_wait / retry_timeout / maximum_lag_on_failover / failsafe_mode | — | none | Patroni/DCS tuning has no counterpart (related: spec.failoverDelay, spec.probes). |
#Pooling, services, networking
| Zalando | CloudNativePG | Kind | Notes |
|---|---|---|---|
spec.enableConnectionPooler | separate Pooler resource | mechanism | ⚠ Zalando default pool mode is transaction; the CNPG default is session — set pgbouncer.poolMode: transaction explicitly. Zalando default 2 instances vs CNPG 1. Applications repoint to the Pooler service. |
spec.enableReplicaConnectionPooler | second Pooler with type: ro | mechanism | |
spec.connectionPooler.* | Pooler.spec.* | transform | numberOfInstances → instances; mode → pgbouncer.poolMode; maxDBConnections → pgbouncer.parameters; resources/image → pod template. |
spec.enableMasterLoadBalancer / enableReplicaLoadBalancer | spec.managed.services.additional[] | mechanism | Add {selectorType: rw or ro, serviceTemplate: {spec: {type: LoadBalancer}}} — default services are never mutated; service DNS names change. |
spec.allowedSourceRanges | …serviceTemplate.spec.loadBalancerSourceRanges | transform | |
spec.serviceAnnotations (+ per-role variants) | service templates / inheritedMetadata.annotations | transform | |
spec.podAnnotations | spec.inheritedMetadata.annotations | transform | Applies to all generated objects. |
#Scheduling and pod attributes
| Zalando | CloudNativePG | Kind | Notes |
|---|---|---|---|
spec.nodeAffinity | spec.affinity.nodeAffinity | direct | CNPG adds anti-affinity controls (podAntiAffinityType, topologyKey). |
spec.tolerations | spec.affinity.tolerations | direct | Note the nesting under affinity. |
spec.podPriorityClassName | spec.priorityClassName | direct | |
spec.schedulerName | spec.schedulerName | direct | |
spec.spiloRunAsUser / spiloRunAsGroup / spiloFSGroup | spec.postgresUID / postgresGID | transform | CNPG default uid/gid 26 (Spilo used 101); normally drop. |
spec.sidecars / spec.initContainers | — | none | Deliberately unsupported. Metrics sidecar → built-in exporter; init logic → bootstrap.initdb.postInit*SQL. |
spec.maintenanceWindows | — | none | Closest: spec.primaryUpdateStrategy: supervised. |
#Backup, clone, standby, TLS, CDC
| Zalando | CloudNativePG | Kind | Notes |
|---|---|---|---|
spec.enableLogicalBackup + schedule | — | none | CNPG backups are physical via ScheduledBackup (⚠ 6-field cron — prepend a seconds field). Keep logical dumps as your own CronJob if needed. |
spec.clone (no timestamp) | spec.bootstrap.pg_basebackup + externalClusters | mechanism | Clone from a live cluster — works against a running Zalando cluster (a supported migration path). |
spec.clone (timestamp / s3 fields) | bootstrap.recovery + externalClusters[].barmanObjectStore | mechanism | Only from a barman-format archive — Spilo WAL-E/WAL-G archives are unreadable by CNPG. |
spec.standby (S3 WAL path) | — | none | Same archive incompatibility. |
spec.standby (host/port) | spec.replica + externalClusters | mechanism | Streaming standby of an external primary; promote by flipping replica.enabled. |
spec.tls | spec.certificates | transform | One-secret model splits into server/client(+replication) certificates; SANs must cover <cluster>-rw.<ns>.svc. Simplest: omit and let CNPG self-manage. |
spec.streams (CDC) | — | none | Re-architect on Publication/Subscription resources or external Debezium (wal_level=logical is the CNPG default). |
#Fork-specific Zalando fields (previous ACP PostgreSQL releases)
| Zalando (ACP fork) | CloudNativePG | Notes |
|---|---|---|
spec.backup (WAL-G schedule/retention/storage) | barman-cloud ObjectStore + spec.plugins + ScheduledBackup + retentionPolicy | First CNPG backup is a fresh full backup. |
spec.exporter / enableExporter | spec.monitoring (built-in exporter) | See the Grafana dashboards guide (How To section). |
spec.enablePgpool2 | — | PgBouncer Pooler only; query load-balancing ≈ -ro service + app routing. |
spec.clusterReplication | spec.replica + externalClusters (+ promotion/demotion tokens) | CNPG replica-cluster (distributed topology) model. |
spec.usersCustomizedPasswd | managed.roles[].passwordSecret | Move passwords out of the CR into basic-auth secrets. |
spec.ipFamilyPrefer | service templates ipFamilies/ipFamilyPolicy | Single-stack IPv6 requires the url.Build fix in the operator (see Configuration). |
#Worked example
Zalando source:
apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
name: acid-orders
spec:
teamId: acid
numberOfInstances: 3
postgresql:
version: "16"
parameters:
shared_buffers: 512MB
max_connections: "300"
volume:
size: 20Gi
storageClass: sc-topolvm
users:
orders_svc: [createdb]
analyst: []
databases:
orders: orders_svc
patroni:
pg_hba: [host all all 10.0.0.0/8 md5]
synchronous_mode: true
synchronous_node_count: 1
resources:
requests: {cpu: "1", memory: 2Gi}
limits: {cpu: "2", memory: 4Gi}
enableConnectionPooler: trueConverted (what the tool emits — a Cluster plus a Pooler):
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: acid-orders
spec:
instances: 3
imageName: <postgresql-16-operand-image> # per your platform registry
inheritedMetadata:
labels:
acid.zalan.do/team: acid
bootstrap:
initdb:
database: orders
owner: orders_svc
managed:
roles:
- {name: analyst, ensure: present, login: true}
- {name: orders_svc, ensure: present, login: true, createdb: true}
postgresql:
parameters:
shared_buffers: 512MB
max_connections: "300"
pg_hba: [host all all 10.0.0.0/8 md5]
synchronous:
method: any
number: 1
dataDurability: preferred # synchronous_mode without 'strict'
storage: {size: 20Gi, storageClass: sc-topolvm}
resources:
requests: {cpu: "1", memory: 2Gi}
limits: {cpu: "2", memory: 4Gi}
---
apiVersion: postgresql.cnpg.io/v1
kind: Pooler
metadata:
name: acid-orders-pooler-rw
spec:
cluster: {name: acid-orders}
type: rw
instances: 2 # Zalando default
pgbouncer:
poolMode: transaction # Zalando default (CNPG default is session!)This exact conversion was verified end-to-end during release
qualification: the converted manifests deployed to a healthy
3-instance cluster, with the bootstrap database/owner, role flags,
synchronous replication (ANY 1 (...)), parameters, and pg_hba
verified by SQL, and writes executed through the Pooler in transaction
mode.
What changes for the application:
| Zalando | CloudNativePG | |
|---|---|---|
| write endpoint | acid-orders.<ns>.svc | acid-orders-rw.<ns>.svc (or the Pooler service) |
| read endpoint | acid-orders-repl.<ns>.svc | acid-orders-ro.<ns>.svc |
| credentials | orders_svc.acid-orders.credentials... secret | acid-orders-app (bootstrap owner) / your basic-auth secrets |