[{"data":1,"prerenderedAt":1995},["ShallowReactive",2],{"/posts/cnpg-migrator":3,"search-api":1994},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"id":10,"date":11,"categories":12,"tags":16,"cover":22,"readingTime":23,"body":28,"_type":1988,"_id":1989,"_source":1990,"_file":1991,"_stem":1992,"_extension":1993},"/posts/cnpg-migrator","posts",false,"","Migrate PostgreSQL to CloudNativePG with CNPG Migrator","A Kubernetes-native, declarative workflow for bringing PostgreSQL into CloudNativePG from AWS RDS or any PostgreSQL running outside your cluster.","cnpg-migrator","2026-07-13",[13,14,15],"engineering","kubernetes","postgresql",[15,17,18,14,19,20,21],"rds","cloudnativepg","migration","helm","open-source","posts/cnpg-1.webp",{"text":24,"minutes":25,"time":26,"words":27},"9 min read",8.715,522900,1743,{"type":29,"children":30,"toc":1965},"root",[31,39,52,77,84,113,130,151,156,173,187,199,203,210,229,239,249,252,258,263,284,317,331,334,340,363,419,424,427,433,439,465,471,484,489,508,521,537,543,571,583,601,606,609,615,620,629,634,786,791,799,802,808,816,868,873,955,960,997,1002,1035,1048,1051,1057,1062,1068,1156,1162,1223,1229,1279,1285,1298,1395,1400,1406,1449,1452,1458,1463,1468,1511,1516,1675,1834,1846,1851,1864,1869,1872,1878,1890,1895,1898,1904,1917,1936,1954,1959],{"type":32,"tag":33,"props":34,"children":35},"element","p",{},[36],{"type":37,"value":38},"text","If your apps run on Kubernetes, you probably want the database lifecycle to look like everything else in the cluster: declarative manifests, GitOps, observable Jobs, and resources you can tune without a separate control plane.",{"type":32,"tag":33,"props":40,"children":41},{},[42,44,50],{"type":37,"value":43},"Two common starting points lead to the same destination: ",{"type":32,"tag":45,"props":46,"children":47},"strong",{},[48],{"type":37,"value":49},"bring your PostgreSQL to Kubernetes",{"type":37,"value":51},".",{"type":32,"tag":53,"props":54,"children":55},"ul",{},[56,67],{"type":32,"tag":57,"props":58,"children":59},"li",{},[60,65],{"type":32,"tag":45,"props":61,"children":62},{},[63],{"type":37,"value":64},"AWS RDS",{"type":37,"value":66},": your apps run on Kubernetes, but the database still lives outside the cluster.",{"type":32,"tag":57,"props":68,"children":69},{},[70,75],{"type":32,"tag":45,"props":71,"children":72},{},[73],{"type":37,"value":74},"PostgreSQL outside Kubernetes",{"type":37,"value":76},": a managed service, a VM, or any Postgres host that is not yet running under the CNPG operator.",{"type":32,"tag":78,"props":79,"children":81},"h3",{"id":80},"physical-import-or-dump-and-restore",[82],{"type":37,"value":83},"Physical import or dump and restore?",{"type":32,"tag":33,"props":85,"children":86},{},[87,89,103,105,111],{"type":37,"value":88},"CNPG can bootstrap a new cluster from an external source using ",{"type":32,"tag":90,"props":91,"children":95},"a",{"href":92,"rel":93},"https://cloudnative-pg.io/docs/1.30/bootstrap#the-externalclusters-section",[94],"nofollow",[96],{"type":32,"tag":97,"props":98,"children":100},"code",{"className":99},[],[101],{"type":37,"value":102},"externalClusters",{"type":37,"value":104}," and ",{"type":32,"tag":97,"props":106,"children":108},{"className":107},[],[109],{"type":37,"value":110},"bootstrap.initdb.import",{"type":37,"value":112},". That works well when the source is a self-managed PostgreSQL you control (for example Postgres on a VM or an in-cluster Helm chart) and you can give CNPG the connection details and privileges it needs.",{"type":32,"tag":33,"props":114,"children":115},{},[116,121,123,128],{"type":32,"tag":45,"props":117,"children":118},{},[119],{"type":37,"value":120},"RDS is the common exception.",{"type":37,"value":122}," It does not expose the privileges required for physical replication, so ",{"type":32,"tag":97,"props":124,"children":126},{"className":125},[],[127],{"type":37,"value":102},{"type":37,"value":129}," bootstrap is not a practical path. For RDS, and for any source where you want a repeatable Job-based workflow with explicit control over ownership, roles, and extensions, the practical path is:",{"type":32,"tag":131,"props":132,"children":133},"blockquote",{},[134],{"type":32,"tag":33,"props":135,"children":136},{},[137,143,145],{"type":32,"tag":97,"props":138,"children":140},{"className":139},[],[141],{"type":37,"value":142},"pg_dump",{"type":37,"value":144}," -> ",{"type":32,"tag":97,"props":146,"children":148},{"className":147},[],[149],{"type":37,"value":150},"pg_restore",{"type":32,"tag":33,"props":152,"children":153},{},[154],{"type":37,"value":155},"That sounds simple until you do it: version-matched client images, parallel restore, progress visibility, failure recovery, and the usual ownership and grants surprises.",{"type":32,"tag":33,"props":157,"children":158},{},[159,161,171],{"type":37,"value":160},"We built ",{"type":32,"tag":45,"props":162,"children":163},{},[164],{"type":32,"tag":90,"props":165,"children":168},{"href":166,"rel":167},"https://github.com/kaskol10/cnpg-migrator",[94],[169],{"type":37,"value":170},"CNPG Migrator",{"type":37,"value":172},", an open-source web UI and Go API that orchestrates dump and restore migrations inside Kubernetes.",{"type":32,"tag":33,"props":174,"children":175},{},[176,178,185],{"type":37,"value":177},"It's logical migration, not zero downtime. What you get is a repeatable workflow on CNPG, where databases, roles, extensions, and backup policies are ",{"type":32,"tag":90,"props":179,"children":182},{"href":180,"rel":181},"https://cloudnative-pg.io/docs/1.30/declarative_database_management#declarative-database-manifest",[94],[183],{"type":37,"value":184},"declared in code",{"type":37,"value":186}," and easier to rightsize after cutover. We've run it on 20+ migrations from RDS and external PostgreSQL into CNPG.",{"type":32,"tag":33,"props":188,"children":189},{},[190,192,197],{"type":37,"value":191},"In our environment (same-region network between the cluster and RDS, custom format dump, default parallelism in the UI), a ~10 GB database usually completes dump and restore in around ",{"type":32,"tag":45,"props":193,"children":194},{},[195],{"type":37,"value":196},"2 minutes",{"type":37,"value":198},". Network latency, source I/O, PVC storage class, and parallel job settings will change that number for you.",{"type":32,"tag":200,"props":201,"children":202},"hr",{},[],{"type":32,"tag":204,"props":205,"children":207},"h2",{"id":206},"who-this-is-for",[208],{"type":37,"value":209},"Who this is for",{"type":32,"tag":33,"props":211,"children":212},{},[213,218,220,227],{"type":32,"tag":45,"props":214,"children":215},{},[216],{"type":37,"value":217},"RDS on AWS, apps on Kubernetes.",{"type":37,"value":219}," Your data plane is still managed outside the cluster: instance classes, snapshots, parameter groups, backup schedules, often expressed as Terraform or CloudFormation. CNPG moves it back in. If you fall behind on engine support, you can also end up on ",{"type":32,"tag":90,"props":221,"children":224},{"href":222,"rel":223},"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/extended-support-charges.html",[94],[225],{"type":37,"value":226},"RDS extended support",{"type":37,"value":228}," and paying for emergency upgrades.",{"type":32,"tag":33,"props":230,"children":231},{},[232,237],{"type":32,"tag":45,"props":233,"children":234},{},[235],{"type":37,"value":236},"PostgreSQL running outside Kubernetes.",{"type":37,"value":238}," Whether it's RDS, a cloud-managed instance, or Postgres on a VM, CNPG Migrator works the same way: point it at the source host, credentials, and port, and restore into a CNPG cluster inside your Kubernetes cluster.",{"type":32,"tag":33,"props":240,"children":241},{},[242,244],{"type":37,"value":243},"The question in both cases is: ",{"type":32,"tag":45,"props":245,"children":246},{},[247],{"type":37,"value":248},"how do you move the data reliably without reinventing the runbook every time?",{"type":32,"tag":200,"props":250,"children":251},{},[],{"type":32,"tag":204,"props":253,"children":255},{"id":254},"why-cnpg-is-worth-betting-on",[256],{"type":37,"value":257},"Why CNPG is worth betting on",{"type":32,"tag":33,"props":259,"children":260},{},[261],{"type":37,"value":262},"Picking a database platform inside a company is a longevity call. You need something you can defend in an architecture review and operate for years.",{"type":32,"tag":33,"props":264,"children":265},{},[266,273,275,282],{"type":32,"tag":90,"props":267,"children":270},{"href":268,"rel":269},"https://cloudnative-pg.io/",[94],[271],{"type":37,"value":272},"CloudNativePG",{"type":37,"value":274}," is a ",{"type":32,"tag":90,"props":276,"children":279},{"href":277,"rel":278},"https://www.cncf.io/projects/cloudnativepg/",[94],[280],{"type":37,"value":281},"CNCF incubating project",{"type":37,"value":283}," that covers the basics well:",{"type":32,"tag":53,"props":285,"children":286},{},[287,297,307],{"type":32,"tag":57,"props":288,"children":289},{},[290,295],{"type":32,"tag":45,"props":291,"children":292},{},[293],{"type":37,"value":294},"HA and failover",{"type":37,"value":296},": the operator manages primary/replica topology and automated failover",{"type":32,"tag":57,"props":298,"children":299},{},[300,305],{"type":32,"tag":45,"props":301,"children":302},{},[303],{"type":37,"value":304},"Backup and restore",{"type":37,"value":306},": scheduled backups to object storage, with restore into new clusters built in",{"type":32,"tag":57,"props":308,"children":309},{},[310,315],{"type":32,"tag":45,"props":311,"children":312},{},[313],{"type":37,"value":314},"Declarative setup",{"type":37,"value":316},": databases, roles, extensions, and cluster configuration in a single Cluster CRD",{"type":32,"tag":33,"props":318,"children":319},{},[320,322,329],{"type":37,"value":321},"Public health and activity metrics are on the ",{"type":32,"tag":90,"props":323,"children":326},{"href":324,"rel":325},"https://insights.linuxfoundation.org/project/cloudnativepg",[94],[327],{"type":37,"value":328},"Linux Foundation Insights dashboard",{"type":37,"value":330},". CNPG Migrator helps you get onto CNPG without a one-off migration script.",{"type":32,"tag":200,"props":332,"children":333},{},[],{"type":32,"tag":204,"props":335,"children":337},{"id":336},"cnpg-migrator-smooth-observable-kubernetes-native",[338],{"type":37,"value":339},"CNPG Migrator: smooth, observable, Kubernetes-native",{"type":32,"tag":33,"props":341,"children":342},{},[343,348,350,355,356,361],{"type":32,"tag":90,"props":344,"children":346},{"href":166,"rel":345},[94],[347],{"type":37,"value":170},{"type":37,"value":349}," wraps ",{"type":32,"tag":97,"props":351,"children":353},{"className":352},[],[354],{"type":37,"value":142},{"type":37,"value":104},{"type":32,"tag":97,"props":357,"children":359},{"className":358},[],[360],{"type":37,"value":150},{"type":37,"value":362}," into a workflow your platform team can reuse.",{"type":32,"tag":364,"props":365,"children":366},"ol",{},[367,372,409,414],{"type":32,"tag":57,"props":368,"children":369},{},[370],{"type":37,"value":371},"You submit source (RDS or any external PostgreSQL) and target (CNPG) config in a UI.",{"type":32,"tag":57,"props":373,"children":374},{},[375,377],{"type":37,"value":376},"The API creates a PVC and a Kubernetes Job:\n",{"type":32,"tag":53,"props":378,"children":379},{},[380,395],{"type":32,"tag":57,"props":381,"children":382},{},[383,388,390],{"type":32,"tag":45,"props":384,"children":385},{},[386],{"type":37,"value":387},"init container:",{"type":37,"value":389}," ",{"type":32,"tag":97,"props":391,"children":393},{"className":392},[],[394],{"type":37,"value":142},{"type":32,"tag":57,"props":396,"children":397},{},[398,403,404],{"type":32,"tag":45,"props":399,"children":400},{},[401],{"type":37,"value":402},"main container:",{"type":37,"value":389},{"type":32,"tag":97,"props":405,"children":407},{"className":406},[],[408],{"type":37,"value":150},{"type":32,"tag":57,"props":410,"children":411},{},[412],{"type":37,"value":413},"The UI shows progress and pod logs.",{"type":32,"tag":57,"props":415,"children":416},{},[417],{"type":37,"value":418},"Optional verification compares source and target database sizes.",{"type":32,"tag":33,"props":420,"children":421},{},[422],{"type":37,"value":423},"The Job is short-lived. The dump lives in a PVC. The target cluster keeps running through CNPG.",{"type":32,"tag":200,"props":425,"children":426},{},[],{"type":32,"tag":204,"props":428,"children":430},{"id":429},"what-makes-these-migrations-tricky",[431],{"type":37,"value":432},"What makes these migrations tricky?",{"type":32,"tag":78,"props":434,"children":436},{"id":435},"_1-client-versions-must-match-the-workload",[437],{"type":37,"value":438},"1) Client versions must match the workload",{"type":32,"tag":33,"props":440,"children":441},{},[442,444,450,452,457,458,463],{"type":37,"value":443},"The migrator uses version-aware ",{"type":32,"tag":97,"props":445,"children":447},{"className":446},[],[448],{"type":37,"value":449},"postgres:\u003Cversion>",{"type":37,"value":451}," images so ",{"type":32,"tag":97,"props":453,"children":455},{"className":454},[],[456],{"type":37,"value":142},{"type":37,"value":104},{"type":32,"tag":97,"props":459,"children":461},{"className":460},[],[462],{"type":37,"value":150},{"type":37,"value":464}," align with source and target. The restore client uses the newer of the two versions. Downgrades (for example PG16 to PG15) can still fail if the dump uses newer features.",{"type":32,"tag":78,"props":466,"children":468},{"id":467},"_2-ownership-and-grants-often-change",[469],{"type":37,"value":470},"2) Ownership and grants often change",{"type":32,"tag":33,"props":472,"children":473},{},[474,476,482],{"type":37,"value":475},"Many dumps are restored as the restore user, so objects shift ownership. By default, the migrator uses ",{"type":32,"tag":97,"props":477,"children":479},{"className":478},[],[480],{"type":37,"value":481},"--no-owner --no-acl",{"type":37,"value":483}," so restores don't fail when roles don't exist on the target yet.",{"type":32,"tag":33,"props":485,"children":486},{},[487],{"type":37,"value":488},"If you need to keep object owners and grants, enable:",{"type":32,"tag":53,"props":490,"children":491},{},[492,500],{"type":32,"tag":57,"props":493,"children":494},{},[495],{"type":32,"tag":45,"props":496,"children":497},{},[498],{"type":37,"value":499},"Preserve ownership and grants",{"type":32,"tag":57,"props":501,"children":502},{},[503],{"type":32,"tag":45,"props":504,"children":505},{},[506],{"type":37,"value":507},"Migrate roles from source",{"type":32,"tag":33,"props":509,"children":510},{},[511,513,519],{"type":37,"value":512},"Role passwords are not copied (",{"type":32,"tag":97,"props":514,"children":516},{"className":515},[],[517],{"type":37,"value":518},"--no-role-passwords",{"type":37,"value":520},"), and RDS system roles are excluded.",{"type":32,"tag":33,"props":522,"children":523},{},[524,526,530,531,535],{"type":37,"value":525},"On our first migrations, we skipped roles and permissions. The restore worked, but applications couldn't connect with the expected grants. We had to redo the migration with ",{"type":32,"tag":45,"props":527,"children":528},{},[529],{"type":37,"value":499},{"type":37,"value":104},{"type":32,"tag":45,"props":532,"children":533},{},[534],{"type":37,"value":507},{"type":37,"value":536}," enabled. Decide your ownership strategy before the maintenance window.",{"type":32,"tag":78,"props":538,"children":540},{"id":539},"_3-extensions-belong-in-the-cnpg-cluster-spec",[541],{"type":37,"value":542},"3) Extensions belong in the CNPG Cluster spec",{"type":32,"tag":33,"props":544,"children":545},{},[546,548,554,556,561,563,569],{"type":37,"value":547},"CNPG provisions extensions via ",{"type":32,"tag":97,"props":549,"children":551},{"className":550},[],[552],{"type":37,"value":553},"databases[].extensions",{"type":37,"value":555},". You don't want ",{"type":32,"tag":97,"props":557,"children":559},{"className":558},[],[560],{"type":37,"value":150},{"type":37,"value":562}," fighting the operator with ",{"type":32,"tag":97,"props":564,"children":566},{"className":565},[],[567],{"type":37,"value":568},"CREATE EXTENSION",{"type":37,"value":570}," again.",{"type":32,"tag":33,"props":572,"children":573},{},[574,576,581],{"type":37,"value":575},"CNPG Migrator defaults to ",{"type":32,"tag":45,"props":577,"children":578},{},[579],{"type":37,"value":580},"skipping extension objects on restore",{"type":37,"value":582},". The workflow is:",{"type":32,"tag":53,"props":584,"children":585},{},[586,591,596],{"type":32,"tag":57,"props":587,"children":588},{},[589],{"type":37,"value":590},"declare extensions in the CNPG Cluster spec",{"type":32,"tag":57,"props":592,"children":593},{},[594],{"type":37,"value":595},"let the operator create them",{"type":32,"tag":57,"props":597,"children":598},{},[599],{"type":37,"value":600},"restore schema and data without re-creating extensions",{"type":32,"tag":33,"props":602,"children":603},{},[604],{"type":37,"value":605},"The operator owns extensions. The migrator owns data. That split keeps the cutover predictable.",{"type":32,"tag":200,"props":607,"children":608},{},[],{"type":32,"tag":204,"props":610,"children":612},{"id":611},"what-youll-see-in-practice",[613],{"type":37,"value":614},"What you'll see in practice",{"type":32,"tag":33,"props":616,"children":617},{},[618],{"type":37,"value":619},"Most of the workflow happens in the UI: configure source and target, pick migration options, then follow progress through dump, restore, and verification.",{"type":32,"tag":33,"props":621,"children":622},{},[623],{"type":32,"tag":624,"props":625,"children":628},"img",{"alt":626,"src":627},"CNPG Migrator new migration form","/images/posts/cnpg-1.webp",[],{"type":32,"tag":33,"props":630,"children":631},{},[632],{"type":37,"value":633},"After you submit a migration, the API creates a Kubernetes Job and PVC. Follow progress from the UI or the cluster:",{"type":32,"tag":635,"props":636,"children":640},"pre",{"className":637,"code":638,"language":639,"meta":7,"style":7},"language-bash shiki shiki-themes catppuccin-frappe github-dark","kubectl get jobs,pvc -n cnpg-migrator\nkubectl logs -n cnpg-migrator job/cnpg-migrator-\u003Cmigration-id> -c pg-dump\nkubectl logs -n cnpg-migrator job/cnpg-migrator-\u003Cmigration-id> -c pg-restore\n","bash",[641],{"type":32,"tag":97,"props":642,"children":643},{"__ignoreMap":7},[644,678,737],{"type":32,"tag":645,"props":646,"children":649},"span",{"class":647,"line":648},"line",1,[650,656,662,667,673],{"type":32,"tag":645,"props":651,"children":653},{"style":652},"--shiki-default:#8CAAEE;--shiki-dark:#B392F0;--shiki-default-font-style:italic;--shiki-dark-font-style:inherit",[654],{"type":37,"value":655},"kubectl",{"type":32,"tag":645,"props":657,"children":659},{"style":658},"--shiki-default:#A6D189;--shiki-dark:#9ECBFF",[660],{"type":37,"value":661}," get",{"type":32,"tag":645,"props":663,"children":664},{"style":658},[665],{"type":37,"value":666}," jobs,pvc",{"type":32,"tag":645,"props":668,"children":670},{"style":669},"--shiki-default:#A6D189;--shiki-dark:#79B8FF",[671],{"type":37,"value":672}," -n",{"type":32,"tag":645,"props":674,"children":675},{"style":658},[676],{"type":37,"value":677}," cnpg-migrator\n",{"type":32,"tag":645,"props":679,"children":681},{"class":647,"line":680},2,[682,686,691,695,700,705,711,716,722,727,732],{"type":32,"tag":645,"props":683,"children":684},{"style":652},[685],{"type":37,"value":655},{"type":32,"tag":645,"props":687,"children":688},{"style":658},[689],{"type":37,"value":690}," logs",{"type":32,"tag":645,"props":692,"children":693},{"style":669},[694],{"type":37,"value":672},{"type":32,"tag":645,"props":696,"children":697},{"style":658},[698],{"type":37,"value":699}," cnpg-migrator",{"type":32,"tag":645,"props":701,"children":702},{"style":658},[703],{"type":37,"value":704}," job/cnpg-migrator-",{"type":32,"tag":645,"props":706,"children":708},{"style":707},"--shiki-default:#81C8BE;--shiki-dark:#F97583",[709],{"type":37,"value":710},"\u003C",{"type":32,"tag":645,"props":712,"children":713},{"style":658},[714],{"type":37,"value":715},"migration-i",{"type":32,"tag":645,"props":717,"children":719},{"style":718},"--shiki-default:#C6D0F5;--shiki-dark:#E1E4E8",[720],{"type":37,"value":721},"d",{"type":32,"tag":645,"props":723,"children":724},{"style":707},[725],{"type":37,"value":726},">",{"type":32,"tag":645,"props":728,"children":729},{"style":669},[730],{"type":37,"value":731}," -c",{"type":32,"tag":645,"props":733,"children":734},{"style":658},[735],{"type":37,"value":736}," pg-dump\n",{"type":32,"tag":645,"props":738,"children":740},{"class":647,"line":739},3,[741,745,749,753,757,761,765,769,773,777,781],{"type":32,"tag":645,"props":742,"children":743},{"style":652},[744],{"type":37,"value":655},{"type":32,"tag":645,"props":746,"children":747},{"style":658},[748],{"type":37,"value":690},{"type":32,"tag":645,"props":750,"children":751},{"style":669},[752],{"type":37,"value":672},{"type":32,"tag":645,"props":754,"children":755},{"style":658},[756],{"type":37,"value":699},{"type":32,"tag":645,"props":758,"children":759},{"style":658},[760],{"type":37,"value":704},{"type":32,"tag":645,"props":762,"children":763},{"style":707},[764],{"type":37,"value":710},{"type":32,"tag":645,"props":766,"children":767},{"style":658},[768],{"type":37,"value":715},{"type":32,"tag":645,"props":770,"children":771},{"style":718},[772],{"type":37,"value":721},{"type":32,"tag":645,"props":774,"children":775},{"style":707},[776],{"type":37,"value":726},{"type":32,"tag":645,"props":778,"children":779},{"style":669},[780],{"type":37,"value":731},{"type":32,"tag":645,"props":782,"children":783},{"style":658},[784],{"type":37,"value":785}," pg-restore\n",{"type":32,"tag":33,"props":787,"children":788},{},[789],{"type":37,"value":790},"When verification is enabled, the UI compares database sizes. That catches gross misses (not logical corruption), but it's enough to confirm the cutover landed:",{"type":32,"tag":33,"props":792,"children":793},{},[794],{"type":32,"tag":624,"props":795,"children":798},{"alt":796,"src":797},"CNPG Migrator verification results and logs","/images/posts/cnpg-2.webp",[],{"type":32,"tag":200,"props":800,"children":801},{},[],{"type":32,"tag":204,"props":803,"children":805},{"id":804},"install-and-run",[806],{"type":37,"value":807},"Install and run",{"type":32,"tag":33,"props":809,"children":810},{},[811],{"type":32,"tag":45,"props":812,"children":813},{},[814],{"type":37,"value":815},"Prerequisites:",{"type":32,"tag":53,"props":817,"children":818},{},[819,832,837,847],{"type":32,"tag":57,"props":820,"children":821},{},[822,824,830],{"type":37,"value":823},"A Kubernetes cluster with the ",{"type":32,"tag":90,"props":825,"children":827},{"href":268,"rel":826},[94],[828],{"type":37,"value":829},"CloudNativePG operator",{"type":37,"value":831}," installed",{"type":32,"tag":57,"props":833,"children":834},{},[835],{"type":37,"value":836},"A target CNPG cluster already created (databases, roles, and extensions declared in the Cluster spec)",{"type":32,"tag":57,"props":838,"children":839},{},[840,845],{"type":32,"tag":45,"props":841,"children":842},{},[843],{"type":37,"value":844},"Network connectivity from your Kubernetes cluster to the source PostgreSQL.",{"type":37,"value":846}," Migration pods must reach the source host on port 5432 (RDS security groups, VPC routing, peering, firewall rules, or VPN as needed)",{"type":32,"tag":57,"props":848,"children":849},{},[850,852,858,860,866],{"type":37,"value":851},"A ",{"type":32,"tag":97,"props":853,"children":855},{"className":854},[],[856],{"type":37,"value":857},"StorageClass",{"type":37,"value":859}," that supports ",{"type":32,"tag":97,"props":861,"children":863},{"className":862},[],[864],{"type":37,"value":865},"ReadWriteOnce",{"type":37,"value":867}," PVCs, sized for your dump",{"type":32,"tag":33,"props":869,"children":870},{},[871],{"type":37,"value":872},"Install with Helm:",{"type":32,"tag":635,"props":874,"children":876},{"className":637,"code":875,"language":639,"meta":7,"style":7},"helm upgrade --install cnpg-migrator oci://ghcr.io/kaskol10/charts/cnpg-migrator \\\n  --version 0.1.4 \\\n  --namespace cnpg-migrator \\\n  --create-namespace\n",[877],{"type":32,"tag":97,"props":878,"children":879},{"__ignoreMap":7},[880,912,930,946],{"type":32,"tag":645,"props":881,"children":882},{"class":647,"line":648},[883,887,892,897,901,906],{"type":32,"tag":645,"props":884,"children":885},{"style":652},[886],{"type":37,"value":20},{"type":32,"tag":645,"props":888,"children":889},{"style":658},[890],{"type":37,"value":891}," upgrade",{"type":32,"tag":645,"props":893,"children":894},{"style":669},[895],{"type":37,"value":896}," --install",{"type":32,"tag":645,"props":898,"children":899},{"style":658},[900],{"type":37,"value":699},{"type":32,"tag":645,"props":902,"children":903},{"style":658},[904],{"type":37,"value":905}," oci://ghcr.io/kaskol10/charts/cnpg-migrator",{"type":32,"tag":645,"props":907,"children":909},{"style":908},"--shiki-default:#F4B8E4;--shiki-dark:#79B8FF",[910],{"type":37,"value":911}," \\\n",{"type":32,"tag":645,"props":913,"children":914},{"class":647,"line":680},[915,920,926],{"type":32,"tag":645,"props":916,"children":917},{"style":669},[918],{"type":37,"value":919},"  --version",{"type":32,"tag":645,"props":921,"children":923},{"style":922},"--shiki-default:#EF9F76;--shiki-dark:#79B8FF",[924],{"type":37,"value":925}," 0.1.4",{"type":32,"tag":645,"props":927,"children":928},{"style":908},[929],{"type":37,"value":911},{"type":32,"tag":645,"props":931,"children":932},{"class":647,"line":739},[933,938,942],{"type":32,"tag":645,"props":934,"children":935},{"style":669},[936],{"type":37,"value":937},"  --namespace",{"type":32,"tag":645,"props":939,"children":940},{"style":658},[941],{"type":37,"value":699},{"type":32,"tag":645,"props":943,"children":944},{"style":908},[945],{"type":37,"value":911},{"type":32,"tag":645,"props":947,"children":949},{"class":647,"line":948},4,[950],{"type":32,"tag":645,"props":951,"children":952},{"style":669},[953],{"type":37,"value":954},"  --create-namespace\n",{"type":32,"tag":33,"props":956,"children":957},{},[958],{"type":37,"value":959},"Access the UI:",{"type":32,"tag":635,"props":961,"children":963},{"className":637,"code":962,"language":639,"meta":7,"style":7},"kubectl port-forward -n cnpg-migrator svc/cnpg-migrator 8080:80\n",[964],{"type":32,"tag":97,"props":965,"children":966},{"__ignoreMap":7},[967],{"type":32,"tag":645,"props":968,"children":969},{"class":647,"line":648},[970,974,979,983,987,992],{"type":32,"tag":645,"props":971,"children":972},{"style":652},[973],{"type":37,"value":655},{"type":32,"tag":645,"props":975,"children":976},{"style":658},[977],{"type":37,"value":978}," port-forward",{"type":32,"tag":645,"props":980,"children":981},{"style":669},[982],{"type":37,"value":672},{"type":32,"tag":645,"props":984,"children":985},{"style":658},[986],{"type":37,"value":699},{"type":32,"tag":645,"props":988,"children":989},{"style":658},[990],{"type":37,"value":991}," svc/cnpg-migrator",{"type":32,"tag":645,"props":993,"children":994},{"style":658},[995],{"type":37,"value":996}," 8080:80\n",{"type":32,"tag":33,"props":998,"children":999},{},[1000],{"type":37,"value":1001},"Fill in:",{"type":32,"tag":53,"props":1003,"children":1004},{},[1005,1015,1025],{"type":32,"tag":57,"props":1006,"children":1007},{},[1008,1013],{"type":32,"tag":45,"props":1009,"children":1010},{},[1011],{"type":37,"value":1012},"Source:",{"type":37,"value":1014}," RDS endpoint or any PostgreSQL host reachable from the cluster, plus port and credentials",{"type":32,"tag":57,"props":1016,"children":1017},{},[1018,1023],{"type":32,"tag":45,"props":1019,"children":1020},{},[1021],{"type":37,"value":1022},"Target:",{"type":37,"value":1024}," CNPG cluster resource name and namespace",{"type":32,"tag":57,"props":1026,"children":1027},{},[1028,1033],{"type":32,"tag":45,"props":1029,"children":1030},{},[1031],{"type":37,"value":1032},"Options:",{"type":37,"value":1034}," ownership, extensions, parallelism, PVC size",{"type":32,"tag":33,"props":1036,"children":1037},{},[1038,1040,1046],{"type":37,"value":1039},"The restore host defaults to ",{"type":32,"tag":97,"props":1041,"children":1043},{"className":1042},[],[1044],{"type":37,"value":1045},"\u003Ccluster-name>-rw.\u003Cnamespace>.svc.cluster.local",{"type":37,"value":1047},", so the CNPG Cluster resource name must match what you enter in the UI.",{"type":32,"tag":200,"props":1049,"children":1050},{},[],{"type":32,"tag":204,"props":1052,"children":1054},{"id":1053},"cutover-checklist",[1055],{"type":37,"value":1056},"Cutover checklist",{"type":32,"tag":33,"props":1058,"children":1059},{},[1060],{"type":37,"value":1061},"Plan a maintenance window. This is the runbook we reuse for RDS and external PostgreSQL cutovers.",{"type":32,"tag":78,"props":1063,"children":1065},{"id":1064},"before-the-window",[1066],{"type":37,"value":1067},"Before the window",{"type":32,"tag":364,"props":1069,"children":1070},{},[1071,1081,1098,1108,1146],{"type":32,"tag":57,"props":1072,"children":1073},{},[1074,1079],{"type":32,"tag":45,"props":1075,"children":1076},{},[1077],{"type":37,"value":1078},"Provision the CNPG cluster",{"type":37,"value":1080}," with databases, roles, and extensions already declared in the Cluster spec.",{"type":32,"tag":57,"props":1082,"children":1083},{},[1084,1089,1091,1096],{"type":32,"tag":45,"props":1085,"children":1086},{},[1087],{"type":37,"value":1088},"Confirm connectivity",{"type":37,"value":1090}," from the ",{"type":32,"tag":97,"props":1092,"children":1094},{"className":1093},[],[1095],{"type":37,"value":10},{"type":37,"value":1097}," namespace to the source PostgreSQL. Test TCP reachability on port 5432 from a debug pod before the window.",{"type":32,"tag":57,"props":1099,"children":1100},{},[1101,1106],{"type":32,"tag":45,"props":1102,"children":1103},{},[1104],{"type":37,"value":1105},"Size the dump PVC",{"type":37,"value":1107}," with headroom above the source database size (compressed dumps are usually smaller, but don't cut it close).",{"type":32,"tag":57,"props":1109,"children":1110},{},[1111,1116,1118],{"type":32,"tag":45,"props":1112,"children":1113},{},[1114],{"type":37,"value":1115},"Run a dry run",{"type":37,"value":1117}," against a staging source when possible:\n",{"type":32,"tag":53,"props":1119,"children":1120},{},[1121,1131,1141],{"type":32,"tag":57,"props":1122,"children":1123},{},[1124,1129],{"type":32,"tag":45,"props":1125,"children":1126},{},[1127],{"type":37,"value":1128},"RDS:",{"type":37,"value":1130}," restore an RDS snapshot to a temporary instance and point Migrator at that endpoint",{"type":32,"tag":57,"props":1132,"children":1133},{},[1134,1139],{"type":32,"tag":45,"props":1135,"children":1136},{},[1137],{"type":37,"value":1138},"VM or self-managed Postgres:",{"type":37,"value":1140}," restore a backup to a staging host, or use a logical replica you can pause",{"type":32,"tag":57,"props":1142,"children":1143},{},[1144],{"type":37,"value":1145},"Use the same PostgreSQL version, extensions, and restore options as production",{"type":32,"tag":57,"props":1147,"children":1148},{},[1149,1154],{"type":32,"tag":45,"props":1150,"children":1151},{},[1152],{"type":37,"value":1153},"Document the rollback path",{"type":37,"value":1155}," (keep the source available until you've validated the target).",{"type":32,"tag":78,"props":1157,"children":1159},{"id":1158},"during-the-window",[1160],{"type":37,"value":1161},"During the window",{"type":32,"tag":364,"props":1163,"children":1164},{},[1165,1175,1185,1195,1213],{"type":32,"tag":57,"props":1166,"children":1167},{},[1168,1173],{"type":32,"tag":45,"props":1169,"children":1170},{},[1171],{"type":37,"value":1172},"Stop application writes",{"type":37,"value":1174}," to the source database (scale deployments to zero, enable maintenance mode, or block at the connection pool).",{"type":32,"tag":57,"props":1176,"children":1177},{},[1178,1183],{"type":32,"tag":45,"props":1179,"children":1180},{},[1181],{"type":37,"value":1182},"Confirm the source is quiesced",{"type":37,"value":1184},": no active writes, replication caught up if applicable.",{"type":32,"tag":57,"props":1186,"children":1187},{},[1188,1193],{"type":32,"tag":45,"props":1189,"children":1190},{},[1191],{"type":37,"value":1192},"Submit the migration",{"type":37,"value":1194}," in the UI (or via API) with the final options: ownership, role migration, extension skip, parallelism, PVC size.",{"type":32,"tag":57,"props":1196,"children":1197},{},[1198,1203,1205,1211],{"type":32,"tag":45,"props":1199,"children":1200},{},[1201],{"type":37,"value":1202},"Watch the Job",{"type":37,"value":1204}," through the UI and ",{"type":32,"tag":97,"props":1206,"children":1208},{"className":1207},[],[1209],{"type":37,"value":1210},"kubectl logs",{"type":37,"value":1212}," until dump and restore complete.",{"type":32,"tag":57,"props":1214,"children":1215},{},[1216,1221],{"type":32,"tag":45,"props":1217,"children":1218},{},[1219],{"type":37,"value":1220},"Run verification",{"type":37,"value":1222}," and compare database sizes. Spot-check critical tables, extensions, and grants if your app depends on them.",{"type":32,"tag":78,"props":1224,"children":1226},{"id":1225},"after-restore-succeeds",[1227],{"type":37,"value":1228},"After restore succeeds",{"type":32,"tag":364,"props":1230,"children":1231},{},[1232,1242,1259,1269],{"type":32,"tag":57,"props":1233,"children":1234},{},[1235,1240],{"type":32,"tag":45,"props":1236,"children":1237},{},[1238],{"type":37,"value":1239},"Smoke-test the CNPG cluster",{"type":37,"value":1241},": connect with the app user, run a few read/write queries, confirm extensions are present.",{"type":32,"tag":57,"props":1243,"children":1244},{},[1245,1250,1252,1257],{"type":32,"tag":45,"props":1246,"children":1247},{},[1248],{"type":37,"value":1249},"Repoint applications",{"type":37,"value":1251}," to the CNPG service (",{"type":32,"tag":97,"props":1253,"children":1255},{"className":1254},[],[1256],{"type":37,"value":1045},{"type":37,"value":1258},") or update Secrets/ConfigMaps your chart reads.",{"type":32,"tag":57,"props":1260,"children":1261},{},[1262,1267],{"type":32,"tag":45,"props":1263,"children":1264},{},[1265],{"type":37,"value":1266},"Bring workloads back",{"type":37,"value":1268}," and watch error rates, connection counts, and query latency.",{"type":32,"tag":57,"props":1270,"children":1271},{},[1272,1277],{"type":32,"tag":45,"props":1273,"children":1274},{},[1275],{"type":37,"value":1276},"Enable CNPG backups",{"type":37,"value":1278}," on the target cluster before you decommission the source.",{"type":32,"tag":78,"props":1280,"children":1282},{"id":1281},"cleanup",[1283],{"type":37,"value":1284},"Cleanup",{"type":32,"tag":33,"props":1286,"children":1287},{},[1288,1290,1296],{"type":37,"value":1289},"Once you're confident in the cutover, delete the migration Job and dump PVC. The UI and API expose this directly (",{"type":32,"tag":97,"props":1291,"children":1293},{"className":1292},[],[1294],{"type":37,"value":1295},"DELETE /api/v1/migrations/{id}/resources",{"type":37,"value":1297},"), or you can remove the resources by name:",{"type":32,"tag":635,"props":1299,"children":1301},{"className":637,"code":1300,"language":639,"meta":7,"style":7},"kubectl delete job cnpg-migrator-\u003Cmigration-id> -n cnpg-migrator\nkubectl delete pvc cnpg-migrator-\u003Cmigration-id> -n cnpg-migrator\n",[1302],{"type":32,"tag":97,"props":1303,"children":1304},{"__ignoreMap":7},[1305,1351],{"type":32,"tag":645,"props":1306,"children":1307},{"class":647,"line":648},[1308,1312,1317,1322,1327,1331,1335,1339,1343,1347],{"type":32,"tag":645,"props":1309,"children":1310},{"style":652},[1311],{"type":37,"value":655},{"type":32,"tag":645,"props":1313,"children":1314},{"style":658},[1315],{"type":37,"value":1316}," delete",{"type":32,"tag":645,"props":1318,"children":1319},{"style":658},[1320],{"type":37,"value":1321}," job",{"type":32,"tag":645,"props":1323,"children":1324},{"style":658},[1325],{"type":37,"value":1326}," cnpg-migrator-",{"type":32,"tag":645,"props":1328,"children":1329},{"style":707},[1330],{"type":37,"value":710},{"type":32,"tag":645,"props":1332,"children":1333},{"style":658},[1334],{"type":37,"value":715},{"type":32,"tag":645,"props":1336,"children":1337},{"style":718},[1338],{"type":37,"value":721},{"type":32,"tag":645,"props":1340,"children":1341},{"style":707},[1342],{"type":37,"value":726},{"type":32,"tag":645,"props":1344,"children":1345},{"style":669},[1346],{"type":37,"value":672},{"type":32,"tag":645,"props":1348,"children":1349},{"style":658},[1350],{"type":37,"value":677},{"type":32,"tag":645,"props":1352,"children":1353},{"class":647,"line":680},[1354,1358,1362,1367,1371,1375,1379,1383,1387,1391],{"type":32,"tag":645,"props":1355,"children":1356},{"style":652},[1357],{"type":37,"value":655},{"type":32,"tag":645,"props":1359,"children":1360},{"style":658},[1361],{"type":37,"value":1316},{"type":32,"tag":645,"props":1363,"children":1364},{"style":658},[1365],{"type":37,"value":1366}," pvc",{"type":32,"tag":645,"props":1368,"children":1369},{"style":658},[1370],{"type":37,"value":1326},{"type":32,"tag":645,"props":1372,"children":1373},{"style":707},[1374],{"type":37,"value":710},{"type":32,"tag":645,"props":1376,"children":1377},{"style":658},[1378],{"type":37,"value":715},{"type":32,"tag":645,"props":1380,"children":1381},{"style":718},[1382],{"type":37,"value":721},{"type":32,"tag":645,"props":1384,"children":1385},{"style":707},[1386],{"type":37,"value":726},{"type":32,"tag":645,"props":1388,"children":1389},{"style":669},[1390],{"type":37,"value":672},{"type":32,"tag":645,"props":1392,"children":1393},{"style":658},[1394],{"type":37,"value":677},{"type":32,"tag":33,"props":1396,"children":1397},{},[1398],{"type":37,"value":1399},"Dump PVCs contain a full copy of your database. Treat them as sensitive.",{"type":32,"tag":78,"props":1401,"children":1403},{"id":1402},"if-restore-fails",[1404],{"type":37,"value":1405},"If restore fails",{"type":32,"tag":364,"props":1407,"children":1408},{},[1409,1419,1429,1439],{"type":32,"tag":57,"props":1410,"children":1411},{},[1412,1417],{"type":32,"tag":45,"props":1413,"children":1414},{},[1415],{"type":37,"value":1416},"Read the restore container logs first.",{"type":37,"value":1418}," Extension conflicts, missing roles, and version mismatches usually show up there.",{"type":32,"tag":57,"props":1420,"children":1421},{},[1422,1427],{"type":32,"tag":45,"props":1423,"children":1424},{},[1425],{"type":37,"value":1426},"Don't repoint applications.",{"type":37,"value":1428}," The source is still your source of truth.",{"type":32,"tag":57,"props":1430,"children":1431},{},[1432,1437],{"type":32,"tag":45,"props":1433,"children":1434},{},[1435],{"type":37,"value":1436},"Fix the target prep",{"type":37,"value":1438}," (add missing roles, declare extensions in the Cluster spec, adjust restore flags) and re-run the migration Job.",{"type":32,"tag":57,"props":1440,"children":1441},{},[1442,1447],{"type":32,"tag":45,"props":1443,"children":1444},{},[1445],{"type":37,"value":1446},"Delete the failed Job and PVC",{"type":37,"value":1448}," before retrying so you start from a clean dump.",{"type":32,"tag":200,"props":1450,"children":1451},{},[],{"type":32,"tag":204,"props":1453,"children":1455},{"id":1454},"declarative-operations-and-rightsizing-after-migration",[1456],{"type":37,"value":1457},"Declarative operations and rightsizing after migration",{"type":32,"tag":33,"props":1459,"children":1460},{},[1461],{"type":37,"value":1462},"The migration Job is the one-off part. What you get on the other side is the long-term win.",{"type":32,"tag":33,"props":1464,"children":1465},{},[1466],{"type":37,"value":1467},"On RDS, rightsizing is constrained. You pick from a fixed instance menu, and changing size often means planning around downtime and cost. With CNPG on Kubernetes, you have more room to tune:",{"type":32,"tag":53,"props":1469,"children":1470},{},[1471,1481,1491,1501],{"type":32,"tag":57,"props":1472,"children":1473},{},[1474,1479],{"type":32,"tag":45,"props":1475,"children":1476},{},[1477],{"type":37,"value":1478},"CPU and memory",{"type":37,"value":1480},": set requests and limits per instance, scale up or down through the Cluster spec",{"type":32,"tag":57,"props":1482,"children":1483},{},[1484,1489],{"type":32,"tag":45,"props":1485,"children":1486},{},[1487],{"type":37,"value":1488},"Storage",{"type":37,"value":1490},": grow PVCs and adjust storage classes without leaving the Kubernetes model",{"type":32,"tag":57,"props":1492,"children":1493},{},[1494,1499],{"type":32,"tag":45,"props":1495,"children":1496},{},[1497],{"type":37,"value":1498},"Topology",{"type":37,"value":1500},": change replica count, affinity, and node placement as workloads evolve",{"type":32,"tag":57,"props":1502,"children":1503},{},[1504,1509],{"type":32,"tag":45,"props":1505,"children":1506},{},[1507],{"type":37,"value":1508},"Operational overhead",{"type":37,"value":1510},": tune the migration Job for the cutover window, then run the CNPG cluster leaner than the old RDS footprint",{"type":32,"tag":33,"props":1512,"children":1513},{},[1514],{"type":37,"value":1515},"A typical pattern from our RDS cutovers:",{"type":32,"tag":1517,"props":1518,"children":1519},"table",{},[1520,1547],{"type":32,"tag":1521,"props":1522,"children":1523},"thead",{},[1524],{"type":32,"tag":1525,"props":1526,"children":1527},"tr",{},[1528,1532,1537,1542],{"type":32,"tag":1529,"props":1530,"children":1531},"th",{},[],{"type":32,"tag":1529,"props":1533,"children":1534},{},[1535],{"type":37,"value":1536},"RDS (before)",{"type":32,"tag":1529,"props":1538,"children":1539},{},[1540],{"type":37,"value":1541},"CNPG (after, week 1)",{"type":32,"tag":1529,"props":1543,"children":1544},{},[1545],{"type":37,"value":1546},"CNPG (after tuning)",{"type":32,"tag":1548,"props":1549,"children":1550},"tbody",{},[1551,1581,1630],{"type":32,"tag":1525,"props":1552,"children":1553},{},[1554,1560,1571,1576],{"type":32,"tag":1555,"props":1556,"children":1557},"td",{},[1558],{"type":37,"value":1559},"Shape",{"type":32,"tag":1555,"props":1561,"children":1562},{},[1563,1569],{"type":32,"tag":97,"props":1564,"children":1566},{"className":1565},[],[1567],{"type":37,"value":1568},"db.t4g.large",{"type":37,"value":1570}," (2 vCPU, 8 GiB)",{"type":32,"tag":1555,"props":1572,"children":1573},{},[1574],{"type":37,"value":1575},"3 instances, conservative requests",{"type":32,"tag":1555,"props":1577,"children":1578},{},[1579],{"type":37,"value":1580},"Adjusted from metrics",{"type":32,"tag":1525,"props":1582,"children":1583},{},[1584,1589,1594,1613],{"type":32,"tag":1555,"props":1585,"children":1586},{},[1587],{"type":37,"value":1588},"CPU",{"type":32,"tag":1555,"props":1590,"children":1591},{},[1592],{"type":37,"value":1593},"Fixed instance size",{"type":32,"tag":1555,"props":1595,"children":1596},{},[1597,1603,1605,1611],{"type":32,"tag":97,"props":1598,"children":1600},{"className":1599},[],[1601],{"type":37,"value":1602},"500m",{"type":37,"value":1604}," request / ",{"type":32,"tag":97,"props":1606,"children":1608},{"className":1607},[],[1609],{"type":37,"value":1610},"2",{"type":37,"value":1612}," limit",{"type":32,"tag":1555,"props":1614,"children":1615},{},[1616,1622,1623,1629],{"type":32,"tag":97,"props":1617,"children":1619},{"className":1618},[],[1620],{"type":37,"value":1621},"250m",{"type":37,"value":1604},{"type":32,"tag":97,"props":1624,"children":1626},{"className":1625},[],[1627],{"type":37,"value":1628},"1",{"type":37,"value":1612},{"type":32,"tag":1525,"props":1631,"children":1632},{},[1633,1638,1642,1659],{"type":32,"tag":1555,"props":1634,"children":1635},{},[1636],{"type":37,"value":1637},"Memory",{"type":32,"tag":1555,"props":1639,"children":1640},{},[1641],{"type":37,"value":1593},{"type":32,"tag":1555,"props":1643,"children":1644},{},[1645,1651,1652,1658],{"type":32,"tag":97,"props":1646,"children":1648},{"className":1647},[],[1649],{"type":37,"value":1650},"2Gi",{"type":37,"value":1604},{"type":32,"tag":97,"props":1653,"children":1655},{"className":1654},[],[1656],{"type":37,"value":1657},"4Gi",{"type":37,"value":1612},{"type":32,"tag":1555,"props":1660,"children":1661},{},[1662,1668,1669,1674],{"type":32,"tag":97,"props":1663,"children":1665},{"className":1664},[],[1666],{"type":37,"value":1667},"1Gi",{"type":37,"value":1604},{"type":32,"tag":97,"props":1670,"children":1672},{"className":1671},[],[1673],{"type":37,"value":1650},{"type":37,"value":1612},{"type":32,"tag":635,"props":1676,"children":1680},{"className":1677,"code":1678,"language":1679,"meta":7,"style":7},"language-yaml shiki shiki-themes catppuccin-frappe github-dark","# excerpt from the CNPG Cluster spec after tuning\nspec:\n  instances: 3\n  resources:\n    requests:\n      cpu: 250m\n      memory: 1Gi\n    limits:\n      cpu: \"1\"\n      memory: 2Gi\n","yaml",[1681],{"type":32,"tag":97,"props":1682,"children":1683},{"__ignoreMap":7},[1684,1693,1708,1726,1738,1751,1769,1787,1800,1817],{"type":32,"tag":645,"props":1685,"children":1686},{"class":647,"line":648},[1687],{"type":32,"tag":645,"props":1688,"children":1690},{"style":1689},"--shiki-default:#737994;--shiki-dark:#6A737D;--shiki-default-font-style:italic;--shiki-dark-font-style:inherit",[1691],{"type":37,"value":1692},"# excerpt from the CNPG Cluster spec after tuning\n",{"type":32,"tag":645,"props":1694,"children":1695},{"class":647,"line":680},[1696,1702],{"type":32,"tag":645,"props":1697,"children":1699},{"style":1698},"--shiki-default:#8CAAEE;--shiki-dark:#85E89D",[1700],{"type":37,"value":1701},"spec",{"type":32,"tag":645,"props":1703,"children":1705},{"style":1704},"--shiki-default:#81C8BE;--shiki-dark:#E1E4E8",[1706],{"type":37,"value":1707},":\n",{"type":32,"tag":645,"props":1709,"children":1710},{"class":647,"line":739},[1711,1716,1721],{"type":32,"tag":645,"props":1712,"children":1713},{"style":1698},[1714],{"type":37,"value":1715},"  instances",{"type":32,"tag":645,"props":1717,"children":1718},{"style":1704},[1719],{"type":37,"value":1720},":",{"type":32,"tag":645,"props":1722,"children":1723},{"style":922},[1724],{"type":37,"value":1725}," 3\n",{"type":32,"tag":645,"props":1727,"children":1728},{"class":647,"line":948},[1729,1734],{"type":32,"tag":645,"props":1730,"children":1731},{"style":1698},[1732],{"type":37,"value":1733},"  resources",{"type":32,"tag":645,"props":1735,"children":1736},{"style":1704},[1737],{"type":37,"value":1707},{"type":32,"tag":645,"props":1739,"children":1741},{"class":647,"line":1740},5,[1742,1747],{"type":32,"tag":645,"props":1743,"children":1744},{"style":1698},[1745],{"type":37,"value":1746},"    requests",{"type":32,"tag":645,"props":1748,"children":1749},{"style":1704},[1750],{"type":37,"value":1707},{"type":32,"tag":645,"props":1752,"children":1754},{"class":647,"line":1753},6,[1755,1760,1764],{"type":32,"tag":645,"props":1756,"children":1757},{"style":1698},[1758],{"type":37,"value":1759},"      cpu",{"type":32,"tag":645,"props":1761,"children":1762},{"style":1704},[1763],{"type":37,"value":1720},{"type":32,"tag":645,"props":1765,"children":1766},{"style":658},[1767],{"type":37,"value":1768}," 250m\n",{"type":32,"tag":645,"props":1770,"children":1772},{"class":647,"line":1771},7,[1773,1778,1782],{"type":32,"tag":645,"props":1774,"children":1775},{"style":1698},[1776],{"type":37,"value":1777},"      memory",{"type":32,"tag":645,"props":1779,"children":1780},{"style":1704},[1781],{"type":37,"value":1720},{"type":32,"tag":645,"props":1783,"children":1784},{"style":658},[1785],{"type":37,"value":1786}," 1Gi\n",{"type":32,"tag":645,"props":1788,"children":1790},{"class":647,"line":1789},8,[1791,1796],{"type":32,"tag":645,"props":1792,"children":1793},{"style":1698},[1794],{"type":37,"value":1795},"    limits",{"type":32,"tag":645,"props":1797,"children":1798},{"style":1704},[1799],{"type":37,"value":1707},{"type":32,"tag":645,"props":1801,"children":1803},{"class":647,"line":1802},9,[1804,1808,1812],{"type":32,"tag":645,"props":1805,"children":1806},{"style":1698},[1807],{"type":37,"value":1759},{"type":32,"tag":645,"props":1809,"children":1810},{"style":1704},[1811],{"type":37,"value":1720},{"type":32,"tag":645,"props":1813,"children":1814},{"style":658},[1815],{"type":37,"value":1816}," \"1\"\n",{"type":32,"tag":645,"props":1818,"children":1820},{"class":647,"line":1819},10,[1821,1825,1829],{"type":32,"tag":645,"props":1822,"children":1823},{"style":1698},[1824],{"type":37,"value":1777},{"type":32,"tag":645,"props":1826,"children":1827},{"style":1704},[1828],{"type":37,"value":1720},{"type":32,"tag":645,"props":1830,"children":1831},{"style":658},[1832],{"type":37,"value":1833}," 2Gi\n",{"type":32,"tag":33,"props":1835,"children":1836},{},[1837,1839,1844],{"type":37,"value":1838},"We started conservative after cutover, watched CPU/memory and query latency for a couple of weeks, then reduced requests. On RDS, dropping from ",{"type":32,"tag":97,"props":1840,"children":1842},{"className":1841},[],[1843],{"type":37,"value":1568},{"type":37,"value":1845}," to a smaller instance class would have been a separate change window.",{"type":32,"tag":33,"props":1847,"children":1848},{},[1849],{"type":37,"value":1850},"Once the data is in, everything else stays declarative:",{"type":32,"tag":53,"props":1852,"children":1853},{},[1854,1859],{"type":32,"tag":57,"props":1855,"children":1856},{},[1857],{"type":37,"value":1858},"databases, roles, and extensions in the Cluster CRD",{"type":32,"tag":57,"props":1860,"children":1861},{},[1862],{"type":37,"value":1863},"backup schedules and object storage policies as operator features",{"type":32,"tag":33,"props":1865,"children":1866},{},[1867],{"type":37,"value":1868},"Start conservative, watch metrics, and adjust the Cluster spec. That's a normal Kubernetes loop.",{"type":32,"tag":200,"props":1870,"children":1871},{},[],{"type":32,"tag":204,"props":1873,"children":1875},{"id":1874},"limitations",[1876],{"type":37,"value":1877},"Limitations",{"type":32,"tag":33,"props":1879,"children":1880},{},[1881,1883,1888],{"type":37,"value":1882},"CNPG Migrator is for ",{"type":32,"tag":45,"props":1884,"children":1885},{},[1886],{"type":37,"value":1887},"logical migration",{"type":37,"value":1889},". Plan a maintenance window. It is not zero-downtime replication.",{"type":32,"tag":33,"props":1891,"children":1892},{},[1893],{"type":37,"value":1894},"The UI has no built-in authentication. Self-host it behind your VPN or an auth proxy. Credentials are passed to Jobs via environment variables today; Kubernetes Secrets integration is on the roadmap. Until then, restrict namespace access, limit RBAC on Jobs and PVCs, and treat dump PVCs as sensitive data.",{"type":32,"tag":200,"props":1896,"children":1897},{},[],{"type":32,"tag":204,"props":1899,"children":1901},{"id":1900},"get-involved",[1902],{"type":37,"value":1903},"Get involved",{"type":32,"tag":33,"props":1905,"children":1906},{},[1907,1909],{"type":37,"value":1908},"Repo: ",{"type":32,"tag":45,"props":1910,"children":1911},{},[1912],{"type":32,"tag":90,"props":1913,"children":1915},{"href":166,"rel":1914},[94],[1916],{"type":37,"value":170},{"type":32,"tag":33,"props":1918,"children":1919},{},[1920,1922,1927,1929,1934],{"type":37,"value":1921},"If you're bringing ",{"type":32,"tag":45,"props":1923,"children":1924},{},[1925],{"type":37,"value":1926},"RDS",{"type":37,"value":1928}," or ",{"type":32,"tag":45,"props":1930,"children":1931},{},[1932],{"type":37,"value":1933},"external PostgreSQL",{"type":37,"value":1935}," into CNPG, give it a try and tell us what you think:",{"type":32,"tag":53,"props":1937,"children":1938},{},[1939,1944,1949],{"type":32,"tag":57,"props":1940,"children":1941},{},[1942],{"type":37,"value":1943},"What extensions did you need?",{"type":32,"tag":57,"props":1945,"children":1946},{},[1947],{"type":37,"value":1948},"How did you handle grants and owners?",{"type":32,"tag":57,"props":1950,"children":1951},{},[1952],{"type":37,"value":1953},"What would you want in a pre-flight check or verification step?",{"type":32,"tag":33,"props":1955,"children":1956},{},[1957],{"type":37,"value":1958},"Issues and PRs welcome.",{"type":32,"tag":1960,"props":1961,"children":1962},"style",{},[1963],{"type":37,"value":1964},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":7,"searchDepth":680,"depth":680,"links":1966},[1967,1968,1969,1970,1971,1976,1977,1978,1985,1986,1987],{"id":80,"depth":739,"text":83},{"id":206,"depth":680,"text":209},{"id":254,"depth":680,"text":257},{"id":336,"depth":680,"text":339},{"id":429,"depth":680,"text":432,"children":1972},[1973,1974,1975],{"id":435,"depth":739,"text":438},{"id":467,"depth":739,"text":470},{"id":539,"depth":739,"text":542},{"id":611,"depth":680,"text":614},{"id":804,"depth":680,"text":807},{"id":1053,"depth":680,"text":1056,"children":1979},[1980,1981,1982,1983,1984],{"id":1064,"depth":739,"text":1067},{"id":1158,"depth":739,"text":1161},{"id":1225,"depth":739,"text":1228},{"id":1281,"depth":739,"text":1284},{"id":1402,"depth":739,"text":1405},{"id":1454,"depth":680,"text":1457},{"id":1874,"depth":680,"text":1877},{"id":1900,"depth":680,"text":1903},"markdown","content:posts:cnpg-migrator.md","content","posts/cnpg-migrator.md","posts/cnpg-migrator","md",[],1783941796503]