Migrating a production PostgreSQL database on Kubernetes is not only about moving data from one operator to another. It is also about choosing the right trade-off between downtime, operational complexity, rollback safety, cost, and business risk.
Practical migration paths from the Crunchy Data PostgreSQL Operator to the Percona Operator for PostgreSQL are described here.
In this method, the Percona cluster is created as a standby and points to the same pgBackRest repository used by the Crunchy Data PostgreSQL Operator cluster. This means the object storage and the path are exactly the same for both clusters.
With the above configuration, the Percona standby restores the initial backup from the shared repository and replays archived WAL. The method is described here.
In this approach, the standby cluster initiates a pg_basebackup for the initial restore and uses native postgres streaming for sync. The method is described here.
In this method, a backup is taken from the Crunchy cluster and restored to a cluster managed by the Percona Operator for PostgreSQL.
In this method, the existing Crunchy Data PostgreSQL Operator primary’s PGDATA persistent volume is used. The process is: stop writes to the Crunchy Data PostgreSQL Operator cluster, delete the cluster while retaining the persistent volume, clear the old persistent volume claim reference, and create a Percona cluster whose PVC selector binds to that same retained PV. PostgreSQL then starts on the existing data directory without a restore.
Each method works, but each one is suitable for a different operational scenario. A migration strategy that is ideal for a small development database may be risky for a large production system with heavy write traffic. Similarly, a method that gives the lowest downtime may require more preparation and validation.
This post compares the four migration approaches so users can choose the most suitable approach.
There is no single best migration strategy for every PostgreSQL workload. A production migration usually has to balance several requirements:
| Same pgBackRest Repository | Streaming Replication | Backup and Restore | Reuse the Persistent volume | |
| Implementation | Primary archives WAL → shared repo → standby does a restore + fetches WAL via pgBackRest archive-get | Primary streams WAL over TCP → standby pg_basebackup + WAL receiver | Take backup from primary -> Restore to new cluster | Same volume is reused by the Percona Operator for PostgreSQL |
| Initial Seed | Restore from an existing backup with pgBackRest | pg_basebackup from live primary | Restore from an existing backup with pgBackRest | Volume contains the entire data |
| Primary to standby sync | WAL is fetched with pgBackRest archive-get | Native PostgreSQL streaming | WAL is fetched with pgBackRest archive-get | N/A |
| Performance impact to the primary | No extra impact | Slight impact due to the pg_basebackup and streaming | No extra impact | Primary will be down for the duration of the migration |
| Network dependencies | No network connectivity is required between the primary and standby clusters. | Network connectivity needed between primary and standby nodes. | No network connectivity is required between the primary and standby clusters. | No Dependency |
| Object storage dependency | Object storage used for WAL should be accessible by both primary and standby | No dependency | Object storage used for WAL should be accessible by both primary and standby | No dependency |
| Downtime | During cutover from the primary to the standby, writes must be blocked until the standby catches up with the primary, or the cutover should be performed during a period of low write activity to allow the standby to catch up. | During cutover from the primary to the standby, writes must be blocked until the standby catches up with the primary, or the cutover should be performed during a period of low write activity to allow the standby to catch up. | During cutover from the primary to the standby, writes must be blocked until the standby catches up with the primary, or the cutover should be performed during a period of low write activity to allow the standby to catch up. | Complete downtime till the new cluster is started |
| Rollback | Use the older crunchy cluster. Easy if there were no writes done on standby. If there were any writes done on standby, data consistency needs to be checked before rolling back. |
Use the older crunchy cluster. Easy if there were no writes done on standby. If there were any writes done on standby, data consistency needs to be checked before rolling back |
Use the older crunchy cluster. Easy if there were no writes done on standby. If there were any writes done on standby, data consistency needs to be checked before rolling back |
Easy to rollback; no issues with data inconsistency, unless the data volume get’s corrupted. |
| Business continuity risks | If migration fails for some reason, it is easy to fall back to the Crunchy Data PostgreSQL Operator cluster. | If migration fails for some reason, it is easy to fall back to the Crunchy Data PostgreSQL Operator cluster. | If migration fails for some reason, it is easy to fall back to the Crunchy Data PostgreSQL Operator cluster. | Can fallback to using the Crunchy Data PostgreSQL Operator cluster if migration fails. If the data volume gets corrupted, full restore needs to be done from backup. RTO/RPO depends on the dataset size and the WAL pushed to the object storage. |
| Cost /Resources utilization | For the duration of migration, there will be 2 clusters which adds up to the resources and the cost. | For the duration of migration, there will be 2 clusters which adds up to the resources and the cost. | For the duration of migration, there will be 2 clusters which adds up to the resources and the cost. | No additional resources / cost needed. |
| Compatible with other / custom backup solution | Backups should be taken with pgbackrest | Any backup solution can be used by the Crunchy Data PostgreSQL Operator | Backups should be taken with pgbackrest | Any backup solution can be used by the Crunchy Data PostgreSQL Operator |
| Client DNS caching issues | Clients might refer to the older entry due to caching entries. This will be reflected for short period of TTL expiry or caching rule set at client after the migration | Clients might refer to the older entry due to caching entries. This will be reflected for short period of TTL expiry or caching rule set at client after the migration | Clients might refer to the older entry due to caching entries. This will be reflected for short period of TTL expiry or caching rule set at client after the migration | No issues |
| Migrating to different kubernetes cluster | Possible | Possible | Possible | Not possible |
There is no “one-size-fits-all” solution for migrating PostgreSQL workloads on Kubernetes. Choosing the right strategy requires a careful balance between acceptable downtime, operational complexity, and business continuity requirements. For example, below there are some scenarios which list the suitable approaches
Before proceeding, we recommend conducting a dry run in a staging environment to validate your chosen method against your specific network topology and workload requirements. By carefully evaluating these trade-offs, you can ensure a secure, efficient transition to the Percona Operator for PostgreSQL.