Logical replication has been part of Postgres since version 10, and the syntax page that governs it is almost comically brief. wants a name, a connection string, a list of publications, and then it offers one innocuous line:That single line expands to more than a dozen options, and several of them change how logical replication uses storage resources. After all, subscriptions created with no clause work perfectly fine. Most subscriptions in the wild don't need these tweaks, and nobody thinks about that line again, if they ever knew it existed at all.Imagine a production system boasting several downstream logical replicas. Consider disk monitors lighting up and flagging the directory. It’s suddenly filling with thousands of anonymous artifacts, and nobody seems to know what is writing there or why. Well, it is Postgres writing in that directory, and the "why" is a longer story.So what lives in that directory? What makes it balloon to terrifying proportions seemingly at random? How is logical replication involved? Is there any way to control or even stop this behavior?The answers lie inside that very same innocuous and esoteric WITH clause. Let's see what's going on here.
Being "the database guy" comes with a lot of questions, and over the last eight months those questions changed. The repetitive ones disappeared, nobody asks how to avoid putting things in the database any more, and the code arriving for review got noticeably more polished. Then this summer a schema landed in front of me with twelve proposed index drops on a single table, which is when I started assuming coding agents over-index. The next schema I looked at had the same shape.
Passing this off as AI slop would be too easy, because most of those changes were competent. So I built a harness and measured it.
I loaded 30 model-generated schemas into PostgreSQL and audited 838 indexes across twelve of them. The competence caught me off guard. Only ten served no requirement I could find; the rest showed solid craft. All four models handled GIN and GiST indexes cleanly, built partial indexes with sensible predicates, and got multi-tenant composite keys in the right order. The baseline SQL quality is much better than what agents wrote a year ago.
Indexes are great, until you pile them onto the single table taking all your writes. On quiet tables you will never notice the difference. On hot tables, every index is extra work on every write.
In one support-tool schema, a model created sixteen indexes on tickets alone. Six of them indexed last_activity_at, a column that updates every time an agent touches a ticket. Compared to my hand-written baseline with seven indexes, the generated schema wrote 1.8× the WAL, took 1.9× longer per update, and pushed up VACUUM time just as much.
Those sixteen indexes were not dumb mistakes. For read queries, they run fast. The problem is that coding agents write indexes query by query, without thinking about write traffic.
What actually happens on disk when you touch that row:
A new agent needs a database login, so the role it logs in as gets the treatment every runbook recommends. Two ALTER ROLE lines go in, one capping how long a single statement may run and one making transactions read-only by default. From the operator's side, the Postgres role for AI agent sessions now looks contained.
alter role agent set statement_timeout = '2s';
ALTER ROLE
alter role agent set default_transaction_read_only = on;
ALTER ROLE
select rolname, rolconfig from pg_roles where rolname='agent';
rolname | rolconfig
---------+---------------------------------------------------------
agent | {default_transaction_read_only=on,statement_timeout=2s}
(1 row)
Both settings bite at the baseline, with SELECT pg_sleep(4) cancelled at 2.060 seconds on the role above and a bare CREATE TABLE refused on a second role carrying only the read-only default. Then the agent's own session answers, in one statement.
SET default_transaction_read_only = off;
SET
SHOW default_transaction_read_only;
default_transaction_read_only
-------------------------------
off
(1 row)
SELECT source FROM pg_settings WHERE name = 'default_transaction_read_only';
source
---------
session
(1 row)
CREATE TABLE app.via_set (i int);
CREATE TABLE
INSERT INTO app.via_set VALUES (1);
INSERT 0 1
One column moved, from user to session, and that column is the whole story. What ALTER ROLE ... SET writes is the value a session starts with; whether the session stays there is up to the session, because every parameter in that opening pair carries a context of user. The quieter route skips SET entirely and needs no SQL at all.
statement_timeout, transaction_timeout, lock_timeout, the two idle timeouts, default_transaction_read_only and application_name all have context = user, so the session the value was meant to restrain is the session allowed to change it.
options=-c statement_timeout=0 arrives with source = client, whichAggregates in PostgreSQL aren't particularly efficient, computationally speaking. It shows most in a scenario where partial aggregation doesn't help: when aggregation only prepares data for the query, processing a large stream of rows and producing, at the output, a not-much-smaller set of groups and the aggregates computed over them. Variable-length types have it worst of all. And the typical example here is SUM(numeric). Built-in aggregates are obliged to handle values in their most general form, whereas in practice the data is often constrained; in the databases of ERP systems such as Microsoft Dynamics or NetSuite, monetary columns of type numeric are usually declared with a fixed scale.Hence the idea of optimising aggregates by tuning them to the specific conditions under which they operate. Previously, this was possible only in a PostgreSQL fork. However, David Rowley recently added a new extension hook in core: SupportRequestSimplifyAggref (commit 42473b3b31, PostgreSQL 19, currently in beta), which lets you pass the planner custom aggregate-transformation logic via planner support functions (prosupport). The mechanism itself has existed since PostgreSQL 12, but it has only now been extended to aggregates. In core, the new request is applied modestly: it replaces COUNT(1) and COUNT(col) over a NOT NULL column with COUNT(*). For an extension, though, it allows almost anything to be done with an aggregate at planning time. That leaves room for interesting technical solutions.So let's put it to work on a live example: a simple extension with a fairly primitive transformation.
I will be teaching the basics of SQL at the 2026 Texas Linuxfest. The session is listed for only 100 minutes, but I wrote the materials for a 3-hour course. We will cover as much of those three hours as the audience can stand (sit?), or they send us off to Sixth Street.
Tickets are available, and this event is great for networking. Ping me if you have questions about this session.
SQL is a powerful language for working with relational databases such as MySQL, PostgreSQL, SQL Server, and Oracle. This is an 80-minute introduction to writing SQL database queries. Please load a copy of DBeaver Community Edition (free, open-source) from https://dbeaver.io/download/ on your Mac, Windows, or Linux laptop to work along with the presentation. We will use the sample database that is included with DBeaver. We will start with simple SELECT statements to retrieve data, INSERT to add data, use UPDATE to modify it, and DELETE to remove it. We will then move on to using WHERE to narrow your database searches, grouping & ordering for readability, and using built-in functions. This is a great way to learn how to use a relational database.
Elizabeth: I know a little bit of your history prior to joining the Postgres project. I think you did work on JPEG — the image specification. The internet thinks that you did some work on libjpeg, which was part of the Mars Perseverance camera work. Tell me a little bit about that and how that stuff affects your work in Postgres.
Tom: So I had nothing to do with the writing of the JPEG specification. But it came out and there were maybe about a dozen of us who were interested in this and said, let's sit down and write an open source implementation of it, which we did, and that became libjpeg. And I was — there was this flurry of activity at the very beginning with maybe about a dozen people involved. Then after that, it kind of went into maintenance mode. I was principal maintainer of it for five years or so, which is why my name is on it more than other people's.
When I got involved in Postgres, that became something that just sucked up all my time. And so I stopped working on libjpeg. I'm happy that some other people picked it up and ran with it, which they did eventually after I ignored it for long enough.
I know for a fact that the engineering cameras on Perseverance use libjpeg, because Joe Conway found an academic paper that said so. They've never been in any direct contact with me.
Elizabeth: Is there crossover between open image specifications and databases?
Tom: Not directly, but it definitely informs my thinking about things like software licenses. I think the fact that JPEG is absolutely everywhere today is 25% the fact that it was a really great standard that lets you make image files about 10 times smaller for the same quality as you could have before and 75% the fact that there was a free implementation that anybody could use. Without that, it would not have been put into the early web browsers and you would not be seeing it all over the net.
We made the right decision on that. And then when I came to Postgres again, the fact that it had a very liberal license was a
[...]Yesterday I had the pleasure of speaking at PGDay UK 2026 in London, at the Cavendish Conference Centre. The talk was called 100,000 Lines of C Later: Re-architecting Enterprise Postgres Backups in Go. It was about the design of a Postgres backup tool: the rules such a tool has to obey, and what those rules look like when you rebuild the tool from scratch in Go, which is what pgSafe is. The slides are available here.
It was very well received, the feedback afterwards was positive, and people were intrigued by the possibilities. For those who weren't there, here is what I talked about.
pgBackRest is one of the two de facto enterprise backup tools for Postgres, with its first stable release dating back to 2016. It started life as Perl and is now written in C, and it has a decade of production lessons incorporated into its code. In April 2026 it lost its corporate backing, and the maintainer archived the repository. A few weeks later, it turned out the project could survive, and it is being maintained again.
Nobody did anything wrong here: funding stopped, and somebody made a reasonable call. But for those few weeks, the question was: what do we back up with?
pgBackRest is about 96,000 lines of dense C, and another 80,000 lines of test harness: manual memory management, custom networking, its own protocols. It is excellent code, maintained by very few people: not many can understand it well enough to work on it.
Critical infrastructure needs alternatives that more people can maintain.
So I started writing one.
pgSafe is written from scratch in Go. It shares pgBackRest's concepts and operational rules, and none of its code. The goal is functionality parity for the common deployments: full and incremental backups, point-in-time recovery, five storage backends (POSIX, S3, Azure Blob, GCS, SFTP), and PostgreSQL 13-18 support. I am happy to announce that the development team (just me for no
[...]
Percona Operator for PostgreSQL 3.1.0 takes on three things that decide whether a PostgreSQL platform passes review: is the data encrypted at rest, can it serve reads without straining the primary, and are the logs there when you need them. This release answers all three inside the custom resource, so none of them is a bolt-on you maintain yourself.
The three headline features are transparent data encryption with pg_tde, logical replicas, and persistent logging. pg_tde encrypts your data on disk, including the write-ahead log. Logical replicas add a read-only copy inside the cluster for reporting and analytics. Persistent logging keeps PostgreSQL and pgBackRest logs across Pod restarts.
The operator is open source and runs on any CNCF-certified Kubernetes distribution. This release also widens where it runs, adding official Rancher Kubernetes Engine (RKE2) support and full ARM64 images. Much of what shipped here comes from requests on forums.percona.com and the public issue tracker.
In this post, you’ll learn about:
Encryption at rest is usually the line item that blocks a database from going into a regulated environment. Storage-level encryption from the cloud provider covers the disk, but it does not protect a copied volume, a leaked backup, or a stray WAL segment, and auditors increasingly want encryption the database itself controls. This release adds transparent data encryption through pg_tde, Percona’s open source TDE extension for PostgreSQL.
With pg_tde, the data in tables, indexes, temporary tables, and the write-ahead log stays encrypted on disk, and PostgreSQL decrypts it only in memory for a session that holds the key. That closes the gaps storage encryption leaves open: a snapshot of the volu
[...]Way back in 2015, I opened a PGXN API issue to allow links between documents rendered by PGXN to work. In 2024 I followed up with another issue to enable relative links to images to work. I mean, everyone wants this, right? It’s how your favorite source code sit works.
Now so does PGXN. As of today, links between documents and to images work. Check it out in the newly-released chdb extension docs, which both link to the chdb and chdb_hook docs and to some nice benchmark graphs.
Or see the Apache Age docs, which includes not only documentation images but also decorative SVGs for the headers.
I’m gradually reindexing all of the extensions so that any other documentation with such links work work; it should be done by tomorrow. Then, at along last, your extensions can look their very best.
We're happy to announce a new Postgres extension: chdb. This extension expands Postgres import and export features via the chDB library, an in-process ClickHouse engine, providing efficient, flexible conversion to and from a wide array of data formats living on your favorite cloud storage systems.
Benchmark {#benchmark}
And boy howdy do we mean efficient! We compared chdb's performance importing the NYC Taxi dataset (1m rows, wide table) in a number of data formats to three other Postgres extensions, all reading from a regionally-colocated AWS S3 bucket. To the chart!
In order to minimize differences and to optimize for measurement of extension performance rather than infrastructure, the chdb, pg_lake, and pg_duckdb benchmarks ran on r8id.xlarge ClickHouse Managed Postgres services with 4 vCPUs and 32 GB RAM; the aws_s3 benchmark ran on a db.r8g.xlarge AWS RDS host, also with 4 vCPUs and 32 GB RAM. Results average three runs for each import. See the benchmark source code for details.
Implementing a zero-trust network model in Kubernetes requires shifting from the default-allow behavior to explicit, label-driven microsegmentation. This hands-on lab walks through securing a standard three-tier architecture (Frontend ⭢ Backend ⭢ Database) using Kubernetes NetworkPolicies, validating both ingress and egress restrictions.
Because standard Kubernetes requires a network plugin to actually enforce these rules, this lab environment uses Calico as the Container Network Interface (CNI). While the YAML manifests are standard Kubernetes API objects, it is the Calico CNI operating under the hood that intercepts the traffic and enforces both our ingress and egress restrictions.
We begin by establishing a baseline three-tier architecture in a dedicated namespace, leveraging specific labels to identify our workloads.
kubectl create namespace production-app
# CREATE FRONTEND POD
kubectl run frontend --image=nginx --labels=tier=frontend -n production-app
# CREATE BACKEND POD
kubectl run backend --image=nginx --labels=tier=backend -n production-app
# CREATE DATABASE POD
kubectl run database --image=postgres:18 --labels=tier=database -n production-app \
--env="POSTGRES_DB=myapp" --env="POSTGRES_USER=appuser" --env="POSTGRES_PASSWORD=securepass123"
Verify the pods and labels:
kubectl get pods -n production-app --show-labels
NAME READY STATUS RESTARTS AGE LABELS
backend 1/1 Running 0 3h6m tier=backend
database 1/1 Running 0 3h12m tier=database
frontend 1/1 Running 0 3h6m tier=frontend
Expose the pods so they can communicate via ClusterIP:
kubectl expose pod frontend --port=80 --target-port=80 -n production-app
kubectl expose pod backend --port=80 --target-port=80 -n production-app
kubectl expose pod database --port=5432 --target-port=5432 -n production-app
The foundation of Kubernetes network security is a namespace-wide d
[...]The PostGIS Team is pleased to release PostGIS 3.7.0rc2! Best Served with PostgreSQL 19 Beta 3 , GEOS 3.15.0 , postgis_tiger_geocoder 2025.2 , and address_standardizer.
This version requires PostgreSQL 14 - 19beta3, GEOS 3.10 or higher, Proj 6.1+, and libgmp. To take advantage of all features, GEOS 3.15+ is needed. To take advantage of all SFCGAL features SFCGAL 2.3.0+ is needed. To use postgis_raster extension GDAL 3+ is required.
This release contains fixes since 3.7.0rc1 release.
Cheat Sheets:
This release is a release candidate of a major release, it includes bug fixes since PostGIS 3.6.4 and new features.
At Swiss PG Day 2026, I had the privilege of moderating a Birds of a Feather (BoF) session. A BoF session is a community-driven event designed for focused, interactive discussion among participants with shared interests.
I gave a brief introduction to the topic "How about being a speaker", taking from my brief experience of giving talks at Postgres community conferences. This was as much about me learning from others as it was to encourage aspiring speakers to submit to the next Call for Papers.
The room filled up with around 20 participants, an ideal group size to keep the dialogue
On 26 August 2026, the Adelaide PostgreSQL User Group met, organized by Robins Tharakan. Shadab Mohammad and Robins Tharakan delivered a talk.
On 26 August, the Sydney PostgreSQL User Group met, organized by Shadab Mohammad. Rajesh Kandasamy and Shadab Mohammad delivered a talk.
On 3 September, the PostgreSQL Istanbul Meetup group met, organized by Devrim Gündüz, Gülçin Yıldırım Jelínek & Bilge Korkmaz Erdim. Volkan Çetin and Önder Kalacı delivered a talk.
PGConf Brazil happened from 2-4 September 2026.
Organized by:
Talk Selection Committee:
Speakers:
We at pgEdge are incredibly proud of our work producing the AI DBA Workbench. It's a monitoring and alerting dashboard with optional AI-driven DBA functionality. But that's the rub, isn't it? The optional AI features are the reason anyone would use it in the first place. It's in the name!It's not simply that AI subscriptions are necessarily expensive, though they can be. There's an additional component of chain-of-custody. Some compliance rules will never allow interacting with an external AI service, or require air-gapped deployments that make such a thing impossible. What then?Great question! The answer can come in a lot of forms, but this time around, let's use llama.cpp. It's a very popular server for running local models. While these are usually not as advanced as a frontier model from OpenAI, Anthropic, or Google, they still offer plenty of value.
An overnight load runs under session_replication_role = replica, the setting most of the popular answers describe as switching enforcement off for the session. It writes an order for customer 999, who does not exist, and then rejects the next row for having a negative amount.
SET session_replication_role = replica;
SET
INSERT INTO orders (customer_id, amount, tenant_id) VALUES (999, 10, 1);
INSERT 0 1
INSERT INTO orders (customer_id, amount, tenant_id) VALUES (1, -5, 1);
ERROR: new row for relation "orders" violates check constraint "orders_amount_check"
Both statements ran one line apart in the same session, against the same table. The parameter governs which triggers and rules fire, and a foreign key is enforced by a pair of internal triggers that pg_trigger names RI_ConstraintTrigger_c_*, so the foreign key falls silent along with them. No trigger implements a CHECK constraint, which is why that one carries on rejecting rows.
Two more levers get recommended for the same job. ALTER TABLE ... DISABLE TRIGGER USER (or ALL) works on the table instead of the session, and PostgreSQL 18 added ALTER TABLE ... ALTER CONSTRAINT ... NOT ENFORCED, which works on a single constraint. All three went through one identical probe set below, on a schema built to carry every kind of rule at once.
session_replication_role = replica a load still meets CHECK, NOT NULL, UNIQUE, identity columns and row-level security. Foreign keys, ON DELETE CASCADE, rules and event triggers go quiet, and a trigger marked ENABLE REPLICA fires for the first time in its life.
RESET nor ENABLE TRIGGER ALL reads a row on the way back, and VALIDATE CONSTRAINT against a foreign key the catalog already believes is valid answers ALTER TABLE while orphans sit in the table.
NOT ENFORCED, added for foreign keys in PostgreSQL 18 and extended to CHECK constraints in 19, is the only lever that scans the table when you switch it back on, and a forced row-level security policy can hide rows from t
If you run PostgreSQL on EC2, you've had two real choices until now: build from source, or fall back to whatever version Amazon Linux itself carries in its base repos. Neither matches what the rest of the PostgreSQL community gets from yum.postgresql.org — the full extension ecosystem, day-one minor releases, and a consistent layout across distributions.
That gap is closed. Amazon Linux 2023 is now a first-class target of the PGDG YUM repository, with its own build root and its own package tree, right next to Enterprise Linux, Fedora, and SUSE.
Summary first:
Number of posts in the past two months
Number of posts in the past two months
Get in touch with the Planet PostgreSQL administrators at planet at postgresql.org.