Self-hosted service
zalando/postgres-operator avatar
zalando/postgres-operator

Zalando Postgres Operator: Kubernetes PostgreSQL Clusters via Manifests

Postgres operator creates and manages PostgreSQL clusters running in Kubernetes

5,254 stars1,072 forksGoMIT

At a glance

What is it?
The Zalando Postgres Operator manages highly available PostgreSQL clusters on Kubernetes through CRDs and Patroni. Here is how it installs, what it does well, and where CloudNativePG is the better fit.
Who is it for?
Adopt the Zalando Postgres Operator if you want PostgreSQL clusters declared as Kubernetes manifests, Patroni streaming replication, PGBouncer pooling, and backup or standby clusters wired into CI/CD without direct API access. Do not adopt it if you want an operator that only owns the database layer: this one also owns connection pooling, TLS, user and credential management, and the Spilo container image, and every one of those is a moving part you inherit.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the Zalando Postgres Operator Solves on Kubernetes

Running PostgreSQL on Kubernetes by hand means writing StatefulSets, wiring replication, handling failover, and remembering which pod is primary after a restart. The Zalando Postgres Operator removes that work by making the cluster the unit of configuration. You describe what you want in a Postgres manifest (a custom resource), and the operator reconciles the StatefulSet, Services, and Patroni configuration to match. The README states the project is "configured only through Postgres manifests (CRDs) to ease integration into automated CI/CD pipelines with no access to Kubernetes API directly, promoting infrastructure as code vs manual operations." That sentence is the whole design thesis. The intended user is a platform or data infrastructure team that already treats Kubernetes as its deployment substrate and wants PostgreSQL to behave like every other workload: reviewed in a pull request, applied by a pipeline, versioned in Git. It is not aimed at a single developer who wants a database on a laptop, and it is not a managed database service. You still run the control plane, the storage classes, and the backup buckets.

Patroni, Spilo and the Manifest Reconciliation Loop

The operator is a Go program that watches custom resources and drives Kubernetes objects. Underneath, the actual PostgreSQL process runs in Spilo, Zalando's container image, and high availability is delegated to Patroni, which the README names as the engine behind the "Streaming replication cluster." The operator does not implement leader election itself; it configures Patroni and lets Patroni decide which instance accepts writes. That split matters when you debug: a failover that looks like an operator problem is usually a Patroni or etcd problem, and the operator logs will only show the manifest diff it applied.

The reconciliation model is diff-based. The operator compares the desired manifest against the live cluster and applies the delta. The README lists what those deltas can include: rolling updates on cluster changes, quick minor version updates, live volume resize without pod restarts on AWS EBS and PVC, and in-place major version upgrades. Connection pooling is handled by a PGBouncer sidecar deployment, and the repository has a dedicated pooler/ directory alongside cmd/, pkg/, manifests/, charts/ and ui/. Credentials and users are managed at the Kubernetes level, which the README frames as easing application deployments. A UI exists for creating and editing cluster manifests, and the CRD sources live under pkg/apis/zalando.org and pkg/apis/acid.zalan.do, with generated CRD YAML committed to manifests/.

Installing the Operator and Creating a First Cluster

The README points at docs/quickstart.md for a first impression and at the quickstart's deployment options section for installation. The repository also ships a charts/ directory, which is where the Helm chart lives, and that is the path most teams take on an existing cluster. The exact chart name and values are not reproduced in the README, so read the chart's own files before running anything.

After the operator is running, the first real use is applying a Postgres manifest. The manifest reference is docs/reference/cluster_manifest.md, and the CRD is the acid.zalan.do kind. The README's supported versions table lists Postgres 14 through 18 for release v2.0.2 and Kubernetes 1.27 or newer, so a version outside that range is a mismatch, not a typo. If you run the operator outside a cluster while developing, the repository includes run_operator_locally.sh at the top level, and the Makefile defines a local target that builds the postgres-operator binary:

bash
make local

The Makefile sets BINARY to postgres-operator by default and builds from cmd/main.go, so the local target is the supported way to produce a binary without a container build.

Backups, Standby Clusters and the Cloud Dependency

Backup and restore are where the operator's cloud assumptions become visible. The README lists restore and cloning of Postgres clusters on AWS, GCS and Azure, logical backups to an S3 or GCS bucket, and standby clusters built from an S3 or GCS WAL archive or from a remote host. Point-in-time recovery runs through pg_basebackup, WAL-G or WAL-E via Spilo. The go.mod file confirms the AWS path is first-class: aws-sdk-go-v2, its config module, and the EC2 service package are direct dependencies, used for volume operations such as the live resize feature.

The README does say the operator is "Configurable for non-cloud environments," and there is a logical-backup/ directory in the repository root. But the backup and restore surface described in the feature list is object-storage shaped. If your environment has no S3-compatible endpoint and no GCS or Azure bucket, you are working outside the documented path and should read docs/administrator.md before assuming the restore story holds. That is not a defect; it is a scope boundary the documentation states rather than hides.

Where the Zalando Operator Is the Wrong Tool

The strongest argument against it is operational surface. Choosing this operator means you also adopt Patroni, Spilo, PGBouncer, and the operator's own user and credential management. Each is a component with its own failure modes and its own upgrade cadence, and the operator's release notes are not the only thing you track. A team that only wants PostgreSQL pods with a primary and a replica, and that already has its own pooling and secret management, is paying for machinery it will not use.

The second constraint is the version window. Release v2.0.2 supports Postgres 14 through 18 and Kubernetes 1.27 and newer. Older Kubernetes clusters are out, and so is Postgres 13, which the v1.15.1 row still lists. The third is migration: the README states plainly that anyone coming from v1.x should read docs/migrate.md before deploying a v2 operator. That document exists because the jump is not transparent. If you cannot schedule a migration window, staying on v1.x is a legitimate decision, but you are then outside the supported matrix of the current release.

Zalando Postgres Operator vs CloudNativePG

CloudNativePG is the comparison people actually search for, and the difference is architectural rather than cosmetic. CloudNativePG builds its high availability into a single operator that manages PostgreSQL instances directly, with no Patroni layer and no separate container image like Spilo standing between the operator and the database. Failover logic lives in the operator's own code. The Zalando operator delegates that responsibility to Patroni and configures it, which is why its HA behaviour is Patroni's behaviour and why its documentation points outward to Spilo and Patroni for the parts it does not own.

The practical consequence is where you look when something breaks. With CloudNativePG there is one component to reason about. With the Zalando operator there are at least two, and the boundary between them is not always obvious from the operator's logs. In exchange, the Zalando operator brings a longer list of bundled capabilities: PGBouncer pooling, a manifest-editing UI, TLS certificate support, OpenShift compatibility, and a documented set of preloaded libraries and extensions including PostGIS, pgvector, pg_cron, pg_partman, pg_repack and timescaledb. If your workload depends on several of those extensions being present and configured, the Zalando image saves integration work. If you want the smallest possible component count, CloudNativePG is the cleaner choice.

Maintenance, Licensing and the Upgrade Path

The repository is not archived, and the last push was on 2026-09-22. Release v2.0.2 shipped on 2026-08-20, following v2.0.1 on 2026-07-29 and v2.0.0 on 2026-07-27. That is a recent cadence, and the README notes the operator "has been developed at Zalando and is being used in production for over five years." The version table is the upgrade contract: each release row pins a Postgres range, a Kubernetes range, and a Golang version. The v2.0.2 row pairs Postgres 14 to 18 with Kubernetes 1.27+ and Golang 1.26.4, which matches the go directive in go.mod. Upgrading the operator therefore means checking three axes, not one.

Licensing is MIT, per the LICENSE file at the repository root. MIT is permissive: it allows commercial use and modification, and it requires that the copyright notice and permission notice be retained in copies or substantial portions. It provides no patent grant and no warranty. Note that the licence covers this repository; Spilo, Patroni and PGBouncer are separate projects with their own licences, and the extensions listed in the README each carry their own. None of this is legal advice, and a deployment that redistributes the container images should have its own review.

Editorial conclusion

Adopt the Zalando Postgres Operator if you want PostgreSQL clusters declared as Kubernetes manifests, Patroni streaming replication, PGBouncer pooling, and backup or standby clusters wired into CI/CD without direct API access. Do not adopt it if you want an operator that only owns the database layer: this one also owns connection pooling, TLS, user and credential management, and the Spilo container image, and every one of those is a moving part you inherit. Before deploying, read docs/migrate.md if you run v1.x, confirm your cluster runs Kubernetes 1.27 or newer, and check that your Postgres version falls in the 14 to 18 range the v2.0.2 release table lists.

Frequently asked questions

What is the Zalando Postgres Operator?

It is a Kubernetes operator that creates and manages highly available PostgreSQL clusters, powered by Patroni and configured only through Postgres manifests (CRDs). It is written in Go and licensed under MIT.

How does the Zalando Postgres Operator compare with CloudNativePG?

The Zalando operator delegates high availability to Patroni and runs PostgreSQL in the Spilo image, so there are more components to reason about. CloudNativePG embeds that logic in a single operator, at the cost of the bundled extras such as PGBouncer pooling and the manifest UI.

How does the Zalando Postgres Operator compare with Crunchy Data's operator?

The README does not discuss other operators, so no comparison can be made from this material. What the README does state is that this operator is configured only through Postgres manifests and is compatible with OpenShift.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. zalando/postgres-operator on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/zalando-postgres-operator.svg)](https://hysenlabs.com/projects/zalando-postgres-operator)