CloudNativePG and Crunchy PGO: an honest, opinionated comparison · Unleashing the Power of Postgres in Kubernetes↓Skip to main content<br>Table of Contents
This article compares CloudNativePG and Crunchy PGO, two of the most adopted<br>open-source operators for running PostgreSQL on Kubernetes. It covers<br>architecture, image design, backup strategy, major version upgrades,<br>observability, licensing and community health. As a co-founder and maintainer<br>of CloudNativePG, I make no claim to neutrality, and I say so upfront. What I<br>can offer is informed bias, grounded in years of daily work on the project and<br>a genuine respect for what Crunchy Data built in this space.<br>For years, I resisted writing a direct comparison between CloudNativePG and<br>Crunchy PGO. It felt like the wrong kind of article to write from where I sit.<br>But after several years of both projects maturing, and particularly since<br>Crunchy Data was acquired by Snowflake,<br>I have been asked with increasing frequency how the two operators compare. I<br>now think the time is right. Last week, I wrote<br>Recipe 24<br>to answer the practical question of how to migrate. This post attempts something<br>harder: an honest assessment of why the two operators differ and what those<br>differences mean for teams choosing a long-term platform for PostgreSQL on<br>Kubernetes.<br>I will acknowledge Crunchy’s legacy, explain the architectural choices that I<br>believe make CloudNativePG the stronger foundation, point to data where it<br>exists, and flag the areas where my view is unavoidably subjective. I will not<br>pretend this is a neutral document.<br>Crunchy’s pioneering role #<br>Crunchy Data released the first PostgreSQL operator for Kubernetes in March<br>2017, less than two years after Kubernetes itself debuted and shortly after<br>CoreOS introduced the operator pattern. That was genuinely ahead of its time. My<br>team at 2ndQuadrant (later acquired by EDB) monitored the ecosystem closely<br>during this period but chose to wait, primarily due to the immaturity of<br>Kubernetes storage primitives. The pivotal moment came in April 2019, when<br>Kubernetes 1.14 introduced stable support for local persistent volumes. Our<br>first cloud-native operator, Cloud Native BDR, followed shortly after, built for<br>active/active workloads using 2ndQuadrant’s bi-directional replication<br>technology (now EDB Postgres Distributed). The<br>first commit<br>to what became CloudNativePG was made on 18 February 2020, by Leonardo Cecchi,<br>Marco Nenciarini and myself.<br>The point is not to minimise what Crunchy built. PGO ran production PostgreSQL<br>on Kubernetes before most people thought that was a reasonable idea, and a large<br>number of teams built their infrastructure on it. That record deserves<br>acknowledgement before any comparison.<br>The architectural divide #<br>The most important difference between the two operators is not a feature. It is<br>a philosophy about where the intelligence for managing PostgreSQL high<br>availability should live.<br>Crunchy PGO delegates HA to Patroni, a<br>Python-based distributed HA manager that runs as a process inside each pod.<br>Patroni is a well-respected project and, in my view, the state of the art for<br>PostgreSQL cluster management on traditional Linux environments. Patroni<br>coordinates failover through a distributed configuration store, which can be<br>etcd, Consul, ZooKeeper or Kubernetes itself, and PGO’s role is primarily to<br>provision and configure what Patroni needs. The operator builds on top of<br>Patroni rather than replacing it. In a Kubernetes context, this means running<br>two sophisticated distributed systems alongside each other, which is a<br>perfectly defensible choice, though one that carries trade-offs our team<br>weighed differently.<br>At KubeCon Salt Lake City in 2024,<br>an engineer from Crunchy explained their reasoning directly:<br>why write complex distributed systems code from scratch when Patroni already<br>existed and was battle-tested? It is a reasonable position, and I understand it.<br>Our team simply reached a different conclusion. We took a fundamentally<br>different decision: to trust the Kubernetes API for exactly what it was designed<br>for (managing distributed systems and applications) and to write the HA logic<br>natively in the operator rather than delegating it to a separate tool.<br>CloudNativePG was designed roughly three years later, with that premise at its<br>core: Kubernetes is the control plane, and the operator should exploit it<br>directly. There is no Patroni, no etcd dependency for HA and no HA framework<br>running in parallel with Kubernetes. The Kubernetes API server is the single<br>source of truth for the state of every resource, including the primary/standby<br>topology. The<br>controller documentation<br>and the<br>technical architecture document<br>describe what that looks like in practice.<br>Direct Pod management #<br>CloudNativePG does not use StatefulSets. It manages Pod and PVC resources<br>directly, which gives the operator granular control that StatefulSets cannot<br>provide. When a failover occurs, CloudNativePG promotes...