
PostgreSQL community images address a real gap in how a Kubernetes database operator earns your trust. Running a database operator on Kubernetes means trusting two things: the code, and the container images the operator pulls. The code is on GitHub, easy to inspect, easy to fork. The container images, the registry that hosts them, and the license that governs them all sit with the vendor, and any of those three can change without the source repository changing at all. Starting with Percona Operator for PostgreSQL 3.0.0, you can run the operator against community images you build yourself from the official PostgreSQL packages on download.postgresql.org, in a registry you control.
In this post:
Open source has changed in the last few years, and not always for the better. Companies have learned that you can keep a project’s source code fully open and still capture most of the lock-in by quietly closing the parts that matter in production: the release artifacts, the container images, the supported OS list, the certified Kubernetes distributions, the marketplace listings.
You can have a fully community CNCF project that does not appear on the Red Hat Marketplace except as a paid Enterprise edition. Similarly, you can have a vendor that ships one packaging in the community and a richer one in Enterprise with the features you actually need in production. The license still says “open source.” The practical experience says “you depend on us.” And the source repository’s license is not the only license that matters here: a vendor can change the license, the trademark policy, or the distribution terms on the container images alone, while leaving the source repository untouched. That has happened in the PostgreSQL operator space recently, and the community noticed.
Nobody outside the vendor can predict when a license will change, when a feature will move behind a paywall, or when an external contribution will get rejected because it competes with an Enterprise feature. Recent history has plenty of examples and the PostgreSQL community has been paying attention. When this community resists vendor-controlled distributions, it is not nostalgia. It is a rational read of where things have gone before.
I work on Percona’s PostgreSQL operator, so I see this conversation from the vendor side. The skepticism is fair. The honest question for us is what to do about it.
Acknowledging the community’s concerns does not mean distributions are pointless. There are real reasons to ship one, and pretending otherwise makes for bad blog posts.
A vendor-built distribution lets the vendor:
If you run the vendor distribution, you accept that the vendor’s registry, image policy, and supported-extension matrix become part of your stack. If the vendor changes any of that, your operator deployment changes with it. That is not hypothetical for users who have lived through it on other products.
So the real question is whether you can keep the benefits a distribution provides for the users who want them, while leaving an honest, supported door open for users who do not. That is the door PGO 3.0.0 opens.
Starting with Percona Operator for PostgreSQL 3.0.0, the operator can run against images built from upstream PostgreSQL packages, not just the Percona Distribution images. This is what we are calling Community PostgreSQL Images. In 3.0.0, the feature ships as a tech preview. In 3.1.0, these images become part of our official release cycle and are fully documented.
One of the main advantages of Community Docker Images is that the community can request or contribute any extension that does not exist in the official Percona PostgreSQL distribution. TimescaleDB and Citus are the first examples: the community asked for them, and we shipped both in the Community Images set from day one.
The operator does not care where the image came from, as long as the image meets the operator’s runtime expectations
A typical CR using a community image looks like this:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
apiVersion: pgv2.percona.com/v2 kind: PerconaPGCluster metadata: name: cluster1 spec: image: registry.example.com/postgresql-community:18 postgresVersion: 18 proxy: pgBouncer: image: registry.example.com/pgbouncer-community:1.23 backups: pgbackrest: image: registry.example.com/pgbackrest-community:2.51 # other spec fields unchanged from a normal CR |
The fields that change are spec.image, spec.proxy.pgBouncer.image, and spec.backups.pgbackrest.image. You can build and publish all three images under your own registry, with your own tags if that helps you track versions. The operator drives the rest of the deployment the same way it always has: instances, backups, replication, monitoring, all of it.
Each Community Docker Image is a thin layer over the chosen base (UBI9 or UBI8) plus the packages the operator needs for that role. Where you see {N}, substitute the PostgreSQL major you build for (17, 18, and so on).
postgres image (e.g. postgres17):
| Package | Role |
|---|---|
postgresql{N}-server |
PostgreSQL server |
postgresql{N}-contrib |
contrib modules |
pg_repack_{N} |
online table/index reorganization |
pgaudit_{N} |
audit logging |
set_user_{N} |
privilege escalation control |
pgvector_{N} |
vector similarity search |
wal2json_{N} |
WAL to JSON logical decoding |
pg_cron_{N} |
in-database cron scheduler |
pgbackrest</code> |
backup/restore tool |
patroni |
HA cluster manager |
timescaledb-2-postgresql-{N} |
time-series extension (x86_64 only; EL9 only for PG18) |
citus_{N} |
distributed PostgreSQL (PG16+ only) |
pgbackrest image:
| Package | Role |
|---|---|
pgbackrest |
backup/restore tool only |
pgbouncer image:
| Package | Role |
|---|---|
pgbouncer |
connection pooler only |
The split is intentional. The postgres image ships the full operator-aware runtime. The backup and proxy images stay minimal. As a result, the operator’s components are in separate failure domains and shrink the attack surface of each container.
A community image is not a Percona Distribution image. Two practical consequences:
Ultimately, these are the right trade-offs. The point of community images is to give you transparency and control. Taking care of your own image is part of that deal. At the same time, we publish all three images under perconalab/percona-postgresql-operator on Docker Hub so you can evaluate the tech preview without standing up your own build pipeline first. perconalab is Percona’s non-production namespace, so use those images for testing. For production, build and sign your own.
UBI9 (EL9):
|
1 2 3 4 5 6 7 8 |
docker.io/perconalab/percona-postgresql-operator:main-postgres14-community docker.io/perconalab/percona-postgresql-operator:main-postgres15-community docker.io/perconalab/percona-postgresql-operator:main-postgres16-community docker.io/perconalab/percona-postgresql-operator:main-postgres17-community docker.io/perconalab/percona-postgresql-operator:main-postgres18-community docker.io/perconalab/percona-postgresql-operator:main-pgbackrest-community docker.io/perconalab/percona-postgresql-operator:main-pgbouncer-community docker.io/perconalab/percona-postgresql-operator:main-upgrade-community |
UBI8 (EL8):
|
1 2 3 4 5 6 |
docker.io/perconalab/percona-postgresql-operator:main-ubi8-postgres14-community docker.io/perconalab/percona-postgresql-operator:main-ubi8-postgres15-community docker.io/perconalab/percona-postgresql-operator:main-ubi8-postgres16-community docker.io/perconalab/percona-postgresql-operator:main-ubi8-postgres17-community docker.io/perconalab/percona-postgresql-operator:main-ubi8-postgres18-community docker.io/perconalab/percona-postgresql-operator:main-ubi8-upgrade-community |
The Dockerfile, the package list, and a sample CI job ship in percona-docker/postgresql-containers/community. The build is a regular make target on top of docker buildx, so you can run it on any multi-platform builder.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Prerequisites: docker buildx with a multi-platform builder docker buildx create --use --name multiarch # Build and push all PostgreSQL community images (UBI9 / EL9) git clone https://github.com/percona/percona-docker cd percona-docker/postgresql-containers/community make all TAG=1.0.0 REGISTRY=myrepo/percona-postgresql-operator # Or a single image make postgres17 TAG=1.0.0 REGISTRY=myrepo/percona-postgresql-operator # UBI8 / EL8 variants make all-ubi8 TAG=1.0.0-ubi8 REGISTRY=myrepo/percona-postgresql-operator |
make all builds all three images (postgres, pgBouncer, pgBackRest) so they stay version-aligned. Override REGISTRY and TAG to point at your own namespace and tagging scheme. Once the images are in your registry, plug them into the CR fields shown earlier, and the operator picks them up.
Full build documentation: percona-docker/postgresql-containers/community/README.md.
Community images live in percona/percona-docker, and the build is driven by a transform.py generator that produces the Dockerfiles under build/. The files under build/ are regenerated on every sync, so contributions go through the generator, never through the generated files.
Full contribution guide: community/CONTRIBUTING.md.
Two channels, depending on the shape of the feedback:
The first step was taking full engineering ownership of Percona Operator for PostgreSQL as an independent project, so the roadmap, the release cadence, and the governance live with one team that the community can talk to directly. Community PostgreSQL Images are the next step in that same commitment. If the community adopts this path, we have ideas for what to invest in next.
We will let the community tell us. If this is useful, we keep investing here. We are ready to add more features to the operator around Community Images. Conversely, if nobody adopts it, that is also a signal, and an honest one.
Try the tech preview in 3.0.0. Open an issue if the build flow is rougher than it should be. Tell us what you want next on the forum or directly on GitHub.
Resources
RELATED POSTS