Latest Blog Posts

Highly Available PostgreSQL on Kubernetes in 10 Minutes
Posted by Hans-Juergen Schoenig in Cybertec on 2026-08-18 at 05:07

"Highly available PostgreSQL" usually means leader election, streaming replicas, automatic failover, health checks, and a lot of careful wiring. With the CYBERTEC PG Operator (CPO) it means a 14-line YAML file. Here's the whole thing, start to finish, on a laptop with minikube.

Every command and output below comes from a tutorial we ran end-to-end on a fresh minikube(Kubernetes 1.30, CPO 0.9.2, PostgreSQL 18.4).

A cluster and an operator

minikube start -p cpo-deploy --driver=docker --cpus=2 --memory=4096

helm repo add cpo https://cybertec-postgresql.github.io/CYBERTEC-operator-tutorials
helm repo update cpo
kubectl create namespace cpo
helm install cpo cpo/postgres-operator -n cpo --version 0.9.2 \
  --set configKubernetes.enable_pod_antiaffinity=false
kubectl -n cpo rollout status deploy/postgres-operator

One information worth knowing: enable_pod_antiaffinity=false flag. By default the operator spreads replicas across different Kubernetes nodes, exactly what you want in production. But minikube is a single node, so without this flag the replica would sit Pending forever. On a real multi-node cluster, leave anti-affinity on.

Describe the database you want

kubectl -n cpo apply -f - <<'EOF'
apiVersion: cpo.opensource.cybertec.at/v1
kind: postgresql
metadata:
  name: pg-cluster
spec:
  dockerImage: 'containers.cybertec.at/cybertec-pg-container/postgres:rocky9-18.4-1'
  numberOfInstances: 2
  postgresql:
    version: '18'
  resources:
    requests: { cpu: 250m, memory: 1Gi }
    limits:   { cpu: '1',  memory: 1Gi }
  teamId: acid
  volume:
    size: 1Gi
EOF

numberOfInstances: 2 is the whole HA story: one leader, one streaming replica. Wait for them:

bash
kubectl -n cpo wait --for=condition=Ready pod \
  -l cluster.cpo.opensource.cybertec.at/name=pg-cluster --timeout=360s
# pod/pg-cluster-0 condition met
# pod/pg-cluster-1 condition met

The "it's actually HA" moment

Ask Patroni, which runs inside every database pod, and what it sees:

kubectl -n cpo exec pg-cluster-0 -- patroni
[...]

All Your GUCs in a Row: jit_debugging_support, jit_dump_bitcode, and jit_profiling_support
Posted by Christophe Pettus in pgExperts on 2026-08-18 at 01:00
Inspect the LLVM bitcode PostgreSQL generates with `jit_dump_bitcode`, or wire JIT-compiled functions into GDB and perf with `jit_debugging_support` and…

Failover slot synchronization in PostgreSQL
Posted by Ajin Cherian in Fujitsu on 2026-08-18 at 00:43

High availability is essential for logical replication environments, but until recently, failover could still leave subscribers disconnected from the replication slots they depend on. PostgreSQL 17 addressed this by introducing failover slot synchronization, allowing logical replication slots to be kept ready on standby servers and reducing the need for full subscriber resynchronization after promotion.

How to Prevent Duplicate Form Submissions in Next.js with Idempotency Keys
Posted by uzair aslam on 2026-08-17 at 20:00

Several duplicate form submissions converging through an idempotency gateway into one database record and one set of downstream integrations

A production-ready approach to preventing duplicate leads and side effects with stable idempotency keys, PostgreSQL constraints, transactional outbox events, safe retries, and reconciliation.

A visitor fills out a form, presses Submit, and sees nothing happen. They press it again. The first request actually succeeded, but its response was delayed. The result can be two leads, two confirmation emails, two CRM updates, and two analytics events from one human action.

This is easy to dismiss as a frontend problem, but duplicate submission is a distributed-systems problem in miniature. Browsers retry, mobile connections fail after the server has committed, serverless functions time out, queues redeliver work, and reconciliation jobs intentionally retry failures. The server cannot infer whether two matching requests represent one action or two deliberate actions unless the client gives both attempts the same identity.

Why disabling the submit button is not enough

Disabling the button is still worthwhile. It improves the interface, stops impatient double-clicks, and tells the visitor that work is in progress. It does not protect the system from a refreshed page, a second browser tab, an automatic client retry, a proxy retry, a function timeout after commit, or a worker processing the same event twice.

The browser guard and the server guarantee solve different problems. Use both, but treat the database guarantee as the source of truth. Anything that depends only on React state disappears when the page reloads and can be bypassed by any direct API client.

The idempotency contract

For every logical submission, the client generates one unpredictable key. Every retry of that submission reuses the key. A genuinely new submission gets a new key. The server scopes the key to

[...]

Sixteen Locks Ought to Be Enough for Anybody
Posted by Christophe Pettus in pgExperts on 2026-08-17 at 16:00
Every query locks every index on a table, even ones it doesn't use.

CNPG Recipe 27 - Running OpenBao on Kubernetes with a CloudNativePG PostgreSQL backend
Posted by Gabriele Bartolini in EDB on 2026-08-17 at 08:48

A walkthrough of running OpenBao on Kubernetes with CloudNativePG as its PostgreSQL storage backend. Every layer of this stack is open source, with no vendor lock-in: Kubernetes and CloudNativePG are both CNCF projects, authenticated entirely over TLS client certificates via the 1.30 DatabaseRole CRD, with no passwords anywhere in the stack. Co-authored with Rob Kenefeck from ControlPlane.

All Your GUCs in a Row: jit_above_cost, jit_inline_above_cost, and jit_optimize_above_cost
Posted by Christophe Pettus in pgExperts on 2026-08-17 at 01:00
PostgreSQL's JIT compiler fires based on estimated query cost, but that estimate measures data volume, not expression complexity.

Why Does PostgreSQL Skip My Index?
Posted by Alexander Ioffe on 2026-08-17 at 00:00
...because your vendor's Cost Model Is from 2002. Since random_page_cost = 4.0 is for spinning disks and hasn't changed in 25 years, setting it to 1.1 cuts this query from 125.98ms to 68.69ms.

How Fast Are Postgres 19 Graph Queries? Part 1: What Are They Actually Doing?
Posted by Alexander Ioffe on 2026-08-17 at 00:00
Postgres 19 adds SQL/PGQ graph queries. Measured against a PostgreSQL 19beta1 build, the fixed-depth graph query compiles to the exact same plan as a hand-written join, and the variable-depth traversal that graph databases were built for still falls to a recursive CTE. Apache AGE runs the same indexed traversal under a Cypher wrapper. Numbers from ExoBench in local mode.

JSONB Paths or GIN or Columns?
Posted by Alexander Ioffe on 2026-08-17 at 00:00
Columns beat JSONB+GIN at everything except multi-key containment on PostgreSQL 17

What Does a Covering Index Cost You?
Posted by Alexander Ioffe on 2026-08-17 at 00:00
'Covering indexes hurt your writes' is an architectural maxim with no number attached. Here is the number: +28% on INSERTs, +25% on UPDATEs, 1.26x faster reads, and covering wins below ~4,300 writes per analytical read. Measured on PostgreSQL 17 at 2M rows.

What Flyway validate actually checks
Posted by Maki Majima on 2026-08-17 at 00:00

Tool capabilities and edition boundaries described here are accurate as of publication. Both vendors move these lines; check current documentation before making decisions based on this post.

There’s a specific moment this post is written for: your pipeline runs flyway validate, everything is green, and you conclude that your database matches your migrations.

That conclusion doesn’t follow. Not because Flyway is broken, but because validate answers a different question than the one you’re asking.

What validate does

When Flyway applies a migration, it computes a checksum of the file (CRC32 for SQL migrations) and stores it in the flyway_schema_history table alongside the version, description, and execution details.

validate recomputes checksums for the local migration files and compares them against what’s stored in the history table. It also checks that applied migrations still exist locally and flags pending or missing ones.

In other words, validate answers: “Have the migration files changed since they were applied, and is the file set consistent with the recorded history?”

It’s a file-integrity check. A good one — it catches the classic incident where someone edits an already-applied migration (even just fixing a typo in a comment) and every environment that applied the original version now disagrees with the repository.

What it never looks at

Notice what’s absent from that description: the schema itself.

validate reads exactly one thing from your database — the history table. It never inspects your actual tables, columns, indexes, or constraints. So:

  • Run a manual ALTER TABLE in production → files unchanged, history unchanged → validate passes
  • Another tool adds an index → validate passes
  • Someone drops a constraint during an incident and forgets → validate passes

None of this is a bug. The tool is doing precisely what it’s scoped to do. The problem is the widespread belief that it’s scoped to do more.

A useful way to hold it in your head:

[...]

All Your GUCs in a Row: jit_expressions and jit_tuple_deforming
Posted by Christophe Pettus in pgExperts on 2026-08-16 at 01:00
PostgreSQL's JIT compiler has two jobs: compiling expressions and deforming tuples. Here's how to isolate JIT bugs with two simple boolean toggles.

pgsonify: Hearing PostgreSQL Health as Elephant Sounds
Posted by Dinesh Kumar on 2026-08-16 at 00:00
pgsonify is an experimental MIT-licensed tool that sonifies PostgreSQL health metrics as real elephant recordings. How it maps stats views to sound.

The agent is not the system. Postgres is.
Posted by Payal Singh in Instaclustr on 2026-08-15 at 21:00

The numbers here come from my own audit and design records as of 2026-08-15. Everything described as a redesign is a plan I have started building and have not proven yet.

Since June I have been building Looper, an experimental system for running long-lived autonomous loops against real work. In July I wrote about the control-theory core: every loop is a closed-loop controller, and the actuator must never reach its own sensor. This post is about the layer above that, and about a change in my mental model that came from measuring the system rather than admiring it. I spent most of the summer designing the organization of the agents, and what turned out to matter was the mechanism that keeps work alive after any particular agent disappears. In the rebuild that mechanism is a Postgres schema: a campaign row per open piece of work, leases with fencing tokens, a state machine enforced by triggers, append-only hash-chained events, and a stored-function API as the only write path. The agents became replaceable workers that Postgres wakes, hands a claim to, and outlives. If you read one section, read "Postgres as the coordination layer." The rest of this post is the evidence for that sentence and what I rebuilt because of it.

What I built, and what held up

The idea, from June, was that I own the goals and everything below me is loops: controllers that sense, act, verify and adjust, each one a small lifecycle that is born from a goal, runs, learns and is retired. Many of them together would form what I called a civilization. I wrote a constitution for it, 18 articles on sensing honesty, independence, containment, lifecycle and the one seat a human never delegates. In August I added a section on warrants, eight more articles: signed, bounded, expiring grants of operational authority that flow down a chain from me to project heads to worker loops.

A good part of that has held up. Thinking in terms of an organization gave me:

  • separation of powers: the thing that acts never grades itself, and the judge c
[...]

The curious case of Google's AlloyDB
Posted by Radim Marek on 2026-08-15 at 15:45

Google launched AlloyDB in 2022. They claimed it is fully compatible with PostgreSQL. Can be up to 100 times faster for analytical queries than vanilla Postgres. Four years later, I haven't personally seen it gain significant traction. But it comes in discussions. When people ask me what AlloyDB actually is, I was able to pin point the features, but wasn't really sure what it delivers.

Over the past 12 months, I’ve evaluated AlloyDB. This article shares my key findings. I tried to keep it as objective as the topic allows, and where it isn't, the text says so. I won't pretend to be objective about the verdict. It depends less on one feature and more on where compatibility ends. And as it goes, "it depends" a lot on your workload and needs.

Let's start with the first claim. What does "fully compatible" mean? In this case it covers the wire protocol. Your psql connects, ORMs work, migration means changing connection string and you are done. What it does not cover is nearly everything you know about the Postgres storage internals and query executor. The 8KB pages that define storage layout for vanilla engine, are no longer the durable representation of your data. The WAL is no longer a recovery mechanism. It becomes the database. VACUUM is there, but runs inside storage layer you don't know and and schedule you can't control. And the tuning options differ from what you are be used to.

This creates the curious case. The compatibility claim is true, and the engineering behind AlloyDB backs it up. It's just narrower than the word "fully" migth suggest. AlloyDB is a different database behind the PostgreSQL protocol.

AlloyDB’s compatibility claim holds at the wire protocol, but the underlying engine diverges immediately at the storage layer.

Gartner defines HTAP, or hybrid transaction/analytical processing, as a new application architecture. It "breaks the wall" between transaction processing and analytics. This allows for better decision-making and real-time insights in business.
The short vers
[...]

All Your GUCs in a Row: jit and jit_provider
Posted by Christophe Pettus in pgExperts on 2026-08-15 at 01:00
PostgreSQL's JIT compiler trades upfront compilation time for faster query execution—but it silently does nothing if the LLVM library isn't installed.

Let's Build a Postgres Extension for Estimating Memory Usage!
Posted by Shaun Thomas in pgEdge on 2026-08-14 at 16:00

Do you like building Postgres extensions? Of course you do! My "Let's Build a Postgres Extension" presentation garnered rave reviews at Postgres Conference 2026 in San Jose and PG Data 2026 in Chicago. But what if you didn't go? Sure the slides are available on both sites, but that's not quite the same, is it?Now that the dust has settled and my long series on Postgres 19 has finally reached its natural conclusion, let's get back to our regularly scheduled shenanigans. It's time to explore the exciting and daunting world of building an extension that does something fun: predicting query memory consumption, and potentially logging or blocking them based on results. Consider this a natural progression of my Introduction to Developing Postgres Extensions article.If you've ever wondered what the "right" setting for work_mem is, you're not alone. Each query node (join, sort, group) gets its own allocation, so large queries can command much more RAM than you might expect. Despite this, there's been no tool to give a rough estimate of how many allocations of a query might require. In the spirit of that missing tool, we'll be building something... semi-capable as a proof-of-concept. It's better to have something than nothing, after all. Unfortunately, almost nothing we need to predict lives in the manual. It lives in the source. Grab a machete, we're going into the untamed jungle.I hope you're ready, because by the time we're done, you'll be a Postgres extension artisan!

Blazing the Trail

Before we can read the map, we need to sketch it out. Building an extension that pokes around inside the planner means we need the Postgres server's own headers, and that means getting the source. You have two honest options here: grab a release tarball straight from the download page, or clone the git mirror to live closer to the metal. We're targeting Postgres 18, so either works fine.You'll also want the build toolchain. The Postgres wiki has the canonical list of everything, though I personally found that libicu dev libraries[...]

Parquet and Iceberg: An Overview
Posted by Joshua Drake in CommandPrompt on 2026-08-14 at 15:06
A pile of containers is not a shipment. A shipment is containers plus a manifest.Apache Iceberg is the manifest. It is not a file format, it is a table format: a metadata layer that records exactly which Parquet files make up a table at every point in time. That one idea buys you things we used to think required a warehouse. Transactions on object storage, so writers never …

All Your GUCs in a Row: io_method and io_workers
Posted by Christophe Pettus in pgExperts on 2026-08-14 at 01:00
Choose your I/O execution engine with `io_method` and size the worker pool with `io_workers`—and yes, you can resize workers without restarting.

PDXPUG Septemter 8, 2026, MeetUp: Migrating to a Temporal Schema
Posted by Mark Wong on 2026-08-13 at 17:54

Please note different time and location this month at UpStart Collective at the U.S. Bancorp Tower (a.k.a. Big Pink). Please RSVP on MeetUp. Tuesday September 8, 2026 from 6:30pm to 8:30pm.

Coinciding with devopsdays Portland, OR, Sept 8-10, 2026.

In v18, Postgres got temporal primary keys, unique constraints, and foreign keys (with `NO ACTION`). Hopefully in v19 we’ll have `UPDATE/DELETE FOR PORTION OF`. So it’s a good time to start thinking about migrating your schema to a temporal structure. Some advantages include:

– Easier queries and joins to reconstruct historical data.
– A better way to do soft-deletes (preserving referential integrity).
– No bugs from foreign key references to since-updated data.
– A less ad hoc way of representing historical data.

This talk will explore how to migrate your schema to include application-time `daterange` and `tstzrange` columns. I’ll use an existing schema for a time-tracking and invoicing application (used by me for over 12 years), showing the pain points of the old structure, not sparing my pride at some bad decisions, and give an approach to bring it all into a nicer temporal structure.

We will also talk about some remaining pain points in using temporal tables, and suggestions to mitigate them.
If you are contemplating a move to temporal tables, this talk will give you an overview of the landscape.

Presented by Paul Jungwirth:

Paul is a freelance software developer in Portland, Oregon.
He has built applications with Postgres since 2010 and is the author of several extensions.
His Postgres contributions include work on GiST indexes, multiranges, and SQL:2011 application-time features.

Postgres in Production Special Series: Diagnosing High Cardinality Workloads in pg_stat_statements (Part 6)
Posted by Ryan Booz in pganalyze on 2026-08-13 at 02:00

In Part 6 of this special Postgres in Production deep dive series, Ryan Booz asks a question that determines how useful pg_stat_statements can be for you at all: do you have a high cardinality workload? This episode covers what that actually means, why ORMs, dynamic SQL, and AI-assisted development tools generate more unique queries than you might expect, a side by side demo of the same workload on Postgres 17 and Postgres 18, and the concrete checks that tell you whether pg_stat_statements is losing the data you need for query tuning.



Share this episode: Click here to share this episode on LinkedIn. Feel free to sign up for our newsletter and subscribe to our YouTube channel.


Transcript

A quick recap

Over the first five episodes we covered what pg_stat_statements is and the metrics it stores (Part 1), what makes a statement “unique” through normalization (Part 2), where the query texts live on disk (Part 3), how new metrics get stored and old ones get deallocated (Part 4), and the configuration settings that control all of it (Part 5).

Through all of that, I have probably mentioned the pg_stat_statements.max setting at least 100 times, because it’s so crucial to understanding how effective the data is that you have. This episode is about the workloads that consistently outrun that setting, and being able to identify whether you’re in that situation is really helpful to determining if pg_stat_statements can help you do the query tuning and optimization that you need it to.

What

[...]

Welcome to pg_walviz: PostgreSQL WAL segment visualizer
Posted by Bertrand Drouvot on 2026-08-13 at 01:00

Introduction

The purpose of this blog post is to introduce pg_walviz, a new tool to visualize PostgreSQL WAL segment files.

pg_waldump is very useful to display a human-readable rendering of the WAL. However, sometimes we also want to see how records are physically stored in a segment: the WAL pages, record fragments, continuation records, alignment padding, block references, full-page images and raw bytes.

Welcome to pg_walviz

It is a read-only tool that displays a WAL segment in a local browser.

It presents the same WAL data at three levels:

  • The segment overview displays all the WAL pages and where the record fragments are located.
  • The WAL Record Fragments panel lists the records present on the selected page.
  • The record inspector and Physical Bytes panels display the record structure and raw bytes.

Those views are synchronized. For example, selecting a page in the segment overview updates the list of WAL record fragments and the physical bytes. Selecting a record does the same for the other views. One can also go directly to a page number, record number, file offset or LSN.

As a picture is worth a thousand words, let’s have a look at it:

pg_walviz overview

As you can see, the colors in the Physical Layout and Physical Bytes panels help to locate the record header, block headers, image headers, relation locator, block number, full-page images, main data and alignment padding.

Moving the mouse over a byte also displays its value, file offset, LSN, WAL page, record, fragment, XID and decoded component.

How to use it

The current release is v0.1.0-beta.1 and can be found in this repository.

One can inspect a WAL segment with:

~/pg_walviz/bin/pg_walviz \
  --pg-waldump /path/to/matching/postgres/bin/pg_waldump \
  /archive/000000010000000000000042

The tool starts a local HTTP server, opens the browser and binds to 127.0.0.1 by default.

No running PostgreSQL server or data directory is required. A directory or several consecutive segment files can also

[...]

All Your GUCs in a Row: io_max_concurrency
Posted by Christophe Pettus in pgExperts on 2026-08-13 at 01:00
PostgreSQL 18's new io_max_concurrency caps per-process I/O operations in flight.

Postgres Checkpoint Followup and Collation Visualization and Codex Luna
Posted by Jeremy Schneider on 2026-08-12 at 21:05

My previous article talked about the checkpoint happiness hint: You probably should not change the checkpoint_timeout setting from its default of 5 minutes.

Checkpoint Followup Questions

A good follow-up question was raised: can an HA replica can save you from downtime if you want to set a large checkpoint_timeout?

It’s true that Postgres allows promoting a replica without restarting, if there’s an unplanned primary restart and your primary is going to take an hour to come back online (after you increased checkpoint_timeout to 45 minutes). But this glosses over the fact that if the replica experiences a restart, then it will take an hour to start up too. Checkpoints on the primary directly translate into restartpoints on the replica (it’s the same WAL stream).

First case: everything is manually managed by a DBA and there’s little automation. Bugs in the tooling are a risk, but the biggest risk here is human error. As we often say in COE’s: people make mistakes. Hoping they won’t make a mistake is not a realistic plan for a reliable platform.

Second case: postgres is increasingly automated and we need to be careful that our automation doesn’t accidentally restart a replica while we’re promoting it.

Even with automation, common Postgres orchestration kits heavily rely on “the DBA knows how to configure it” (ie. you still can’t trust all of the defaults). One example: PG configuration changes require rolling restarts. Is the default behavior of common orchestration frameworks to continue a rolling restart even if the first node never comes back up? Are we back to the first case of relying on the DBAs to know the specific incantation of special commands they need to run, to ensure they never accidentally end up restarting both nodes? If the rolling restart can’t complete, will the DBA know how to address it without accidentally triggering a restart in any way?

And what if a query is triggering a postgres bug which causes a restart – like consuming enough memory to trigger OOM? This is r

[...]

Introducing the CYBERTEC PG Operator
Posted by Hans-Juergen Schoenig in Cybertec on 2026-08-12 at 09:05

There is no shortage of PostgreSQL operators for Kubernetes. Projects such as CloudNativePG, the Zalando postgres-operator, Crunchy PGO, and StackGres have all helped shape the ecosystem.

So why did we build another one?

Our answer is simple: multi-site PostgreSQL along with other capabilities.

The CYBERTEC PG Operator delivers the PostgreSQL lifecycle management expected from a Kubernetes operator, while placing a strong focus on operating PostgreSQL across multiple Kubernetes locations through Multi-site Clusters.

From cross-site replication and automated failover to validated deployment models, architecture guidance, and operational documentation, Multi-site Clusters were developed to address challenges that arise when PostgreSQL extends beyond a single Kubernetes environment.

The CYBERTEC PG Operator is open source, built on proven PostgreSQL technologies, and backed by CYBERTEC's PostgreSQL expertise.

We are excited to share the project with the community and look forward to your feedback and contributions.

Explore the source here: https://github.com/cybertec-postgresql/CYBERTEC-pg-operator

The post Introducing the CYBERTEC PG Operator appeared first on CYBERTEC PostgreSQL | Services & Support.

All Your GUCs in a Row: io_combine_limit and io_max_combine_limit
Posted by Christophe Pettus in pgExperts on 2026-08-12 at 01:00
PostgreSQL 17 introduced read streams that combine adjacent disk blocks into larger I/O requests.

Introducing sqlfmt: an SQL gofmt-style formatter
Posted by Dimitri Fontaine on 2026-08-11 at 12:30

Formatting SQL tends to bring some of the same questions again and again: should we uppercase clause keywords? should we put the separating comma at the start of a line to ease refactoring? how to align the SQL clauses with one-another?

Over the years I have grown my own SQL style and didn’t find tooling that would implement it. Also, I’ve been asked here and there if there is a tool that would replicate The Art of PostgreSQL SQL indentation style… and now there is finally a good answer to that question!

sqlfmt is a gofmt-style formatter that implements my own favorite SQL indentation style. One opinionated style, no configuration knobs. Run it, commit the result, move on.

Multi-tenant BYOK encryption in PostgreSQL with pgcrypto
Posted by Tudor Golubenco in Xata on 2026-08-11 at 12:00
Implement multi-tenant BYOK column encryption in PostgreSQL with pgcrypto using customer-managed encryption keys.

Multi-Region PostgreSQL Disaster Recovery and Failback with Crunchy PGO
Posted by Wellingtone Luvonga in Cybertec on 2026-08-11 at 05:00

You have probably read a dozen tutorials on setting up PostgreSQL High Availability (HA). On paper, it looks simple: spin up a primary instance, spin up a standby, and let them replicate.

But what happens when you move to a multi-region architecture? What happens when your disaster recovery (DR) backup utility (like pgBackRest) strictly demands secure TLS (HTTPS) endpoints, but configuring native SSL on local storage/MinIO is an administrative nightmare? More importantly, when disaster strikes and you fail over to your DR region, how do you successfully fail back to your original primary region without running into timeline conflicts, operator deadlocks, or S3 archive poisoning?

This comprehensive guide takes you through the entire lifecycle—from the initial secure bootstrap to simulated regional failure, active failover, and the highly complex process of reversing roles (failback) using the Crunchy  PostgreSQL Operator (PGO) on Kubernetes and MinIO as the secure WAL repository.

Multi region PostgreSQL

Phase 1: The Secure TLS Gateway (NGINX Reverse Proxy)

pgBackRest is extremely strict: it expects secure S3 endpoints over HTTPS. However, setting up native TLS directly on a local MinIO deployment is often over-engineered and tedious.

We solve this by deploying a lightweight NGINX Reverse Proxy in our minio namespace. This proxy terminates SSL/TLS on port 443 using a self-signed certificate and cleanly forwards plain HTTP traffic to MinIO on port 9000.

Generate the Self-Signed Certificate

Generate a certificate valid for the proxy's in-cluster DNS name ( minio-secure.minio.svc.cluster.local):

# Generate the private key and self-signed certificate
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout tls.key \
  -out tls.crt \
  -subj "/CN=minio-secure.minio.svc.cluster.local" \
  -addext "subjectAltName = DNS:minio-secure.minio.svc.cluster.local"

# Save the TLS certificate inside the minio namespace
kubectl create secret tls minio-secure-tls \
  --key tls.key \
  --cert tls.crt \
  -n min
[...]

Top posters

Number of posts in the past two months

Top teams

Number of posts in the past two months

Feeds

Planet

  • Policy for being listed on Planet PostgreSQL.
  • Add your blog to Planet PostgreSQL.
  • List of all subscribed blogs.
  • Manage your registration.

Contact

Get in touch with the Planet PostgreSQL administrators at planet at postgresql.org.