TL;DR
- VACUUM ANALYZE ran more than 2x faster on OrioleDB.
- Primary-key lookups skip the extra heap fetch entirely, since data lives inside the primary key B-tree instead of a separate heap.
- Smaller storage footprint than the heap table at the same row count, even while holding roughly 4x more dead tuples.
- Runs on your existing CNPG operator - a different container image and a couple of config changes, nothing more.
- Migration is per-table, not all-or-nothing. Heap tables and OrioleDB tables sit in the same database without conflict.
As a database engineer at CloudRaft, I frequently evaluate new technologies to find better solutions for optimizing database performance.
During a recent optimization task for a client running CloudNativePG (CNPG), I ran into a problem that eventually led me down the path to writing this article: keeping up with VACUUM on a write-heavy table. When you're dealing with resource-heavy workloads with massive write volumes, managing autovacuum routines and fighting table bloat feels like a never-ending battle. That struggle is how I first found out about OrioleDB - an engine created by Alexander Korotkov, an active PostgreSQL Major Contributor and Committer.
What is OrioleDB?
OrioleDB is a pluggable storage engine for PostgreSQL, built to fix specific bottlenecks in how standard PostgreSQL handles memory and write-heavy workloads like table bloat from constant updates, vacuum overhead, and lock contention on hardware.
Where PostgreSQL's Memory Model Breaks Down
Before looking further, let’s first understand some internals of PostgreSQL memory structure and its working.
PostgreSQL caches table pages in shared_buffers, tracked through a shared-memory hash table. Every access to a page, even a read - has to go through that hash table under locks and atomic operations to find where the page lives in memory. On servers with many CPU cores, this becomes a bottleneck: concurrent queries end up contending for the same locks instead of running in parallel.
What OrioleDB solves
OrioleDB replaces that lookup with a direct pointer from each in-memory page to its data, removing the shared hash table entirely. Page reads no longer require locks or atomic operations, so throughput scales with core count instead of flattening out under contention. Updates also work differently at the memory level: instead of leaving old row versions behind, changes go through an undo log, keeping memory and storage cleaner.
- Normal Postgres stores table rows in something called a "heap," and it uses old-row-versions + a background cleanup process called VACUUM to manage updates/deletes. Under heavy update/delete workloads, this causes bloat (wasted disk, slower queries over time) and VACUUM overhead.
- OrioleDB replaces how rows are physically stored — a different storage engine underneath (undo-log based, no heap bloat, designed to also support storing data directly on S3).
Using OrioleDB with CloudNativePG (CNPG)
This evaluation aims to:
- How to run OrioleDB using CloudNativePG (CNPG).
- Measure real performance differences against standard PostgreSQL under write-heavy and concurrent workloads.
- Assess operational readiness - failover, backup, and recovery.
Demo of OrioleDB + CNPG
To use OrioleDB, you first need an updated PostgreSQL container image that has OrioleDB library installed so that you can enable the orioledb extension and use it.
| Level | Can You Reuse Existing? | Explanation |
|---|---|---|
| Kubernetes & CNPG Operator | Yes, Reuse Existing | You do not need to install a new CNPG operator. The existing CNPG Operator running in your K8s cluster can manage OrioleDB clusters alongside standard Postgres clusters seamlessly. |
Existing Database Instance (Cluster CRD) | Need a New Cluster CRD | You cannot simply run CREATE EXTENSION orioledb; on your existing vanilla PostgreSQL pod. OrioleDB requires a patched PostgreSQL binary/image and shared_preload_libraries = 'orioledb'. |
| Inside the OrioleDB Database | Hybrid Coexistence | Inside an OrioleDB cluster, you can run both standard tables (USING heap) and OrioleDB tables (USING orioledb) in the same database. |
How do you use OrioleDB with CloudNativePG?
Spin up a new CNPG Cluster custom resource (cluster-orioledb) side-by-side in the same Kubernetes cluster, test it safely, and replicate data over from your existing cluster without risking your live production database.
You only need 3 things to run OrioleDB with your existing CNPG setup:
Step 1: Use an OrioleDB-Enabled Container Image
Standard CNPG images (ghcr.io/cloudnative-pg/postgresql) do not include the OrioleDB extension and engine hooks. You must point imageName to an OrioleDB image:
…
imageName: orioledb/orioledb:16
…
yaml
Step 2: Configure Postgres Memory & Preload Libraries
In the CNPG Cluster spec under postgresql.parameters:
postgresql:
parameters:
shared_preload_libraries: 'orioledb'
# Allocate ~50% of container memory to OrioleDB buffer pool
orioledb.main_buffers: '8GB'
orioledb.undo_buffers: '2GB'
# Reduce standard shared_buffers (only needed for system catalogs)
shared_buffers: '1GB'
yaml
Step 3: Deploy and Enable the Extension
Apply the manifest to Kubernetes, connect to the database, and run:
CREATE EXTENSION IF NOT EXISTS orioledb;
-- Now create your high-write tables using the new engine:
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
customer_id INT,
amount NUMERIC,
status TEXT
) USING orioledb;
sql
OR
Change the access method
ALTER TABLE your_existing_table_name SET ACCESS METHOD orioledb;
sql
OR Add this parameter to your CNPG postgresql.parameters YAML block:
default_table_access_method: 'orioledb'
yaml
How did OrioleDB perform in our test?
- Benchmarking Storage Bloat and Maintenance Overhead: Postgres Heap vs. OrioleDB.
kubectl exec -n pg17-test pg17-test-1 -- psql -U postgres -d app -c "
SELECT pg_size_pretty(pg_total_relation_size('pgbench_accounts')) AS total_size,
n_dead_tup, n_live_tup
FROM pg_stat_user_tables WHERE relname='pgbench_accounts';"
total_size | n_dead_tup | n_live_tup
------------+------------+------------
759 MB | 82742 | 4999500
(1 row)
kubectl exec -n orioledb-test orioledb-test-1 -- psql -U postgres -d app -c "
SELECT pg_size_pretty(pg_total_relation_size('pgbench_accounts')) AS total_size,
n_dead_tup, n_live_tup
FROM pg_stat_user_tables WHERE relname='pgbench_accounts';"
total_size | n_dead_tup | n_live_tup
------------+------------+------------
723 MB | 341087 | 4999954
(1 row)
kubectl exec -n pg17-test pg17-test-1 -- psql -U postgres -d app -c "\timing on" -c "VACUUM ANALYZE pgbench_accounts;"
Timing is on.
VACUUM
Time: 13811.359 ms (00:13.811)
kubectl exec -n orioledb-test orioledb-test-1 -- psql -U postgres -d app -c "\timing on" -c "VACUUM ANALYZE pgbench_accounts;"
Timing is on.
VACUUM
Time: 6013.781 ms (00:06.014)
bash
-
More than 2x Faster Maintenance Operations: The VACUUM ANALYZE command executes over twice as fast on OrioleDB (taking 6.01 seconds compared to 13.81 seconds on standard Postgres), significantly minimizing overhead during database maintenance windows.
-
Highly Efficient Storage Footprint: OrioleDB consumes less disk space (723 MB vs 759 MB), demonstrating tighter page packing and data layout optimization for the exact same volume of ~5 million live rows.
-
Superior Resilience to Accumulating Dead Tuples: OrioleDB maintains a smaller storage footprint and faster vacuum times even while holding four times more dead tuples (341,087 dead tuples vs 82,742 in standard Postgres). This showcases the massive performance benefit of OrioleDB's built-in undo logs, which handle heavy write/update bloat far better than the traditional PostgreSQL heap engine.
-
Index-organized tables: data physically stored inside the primary-key B-tree (like MySQL InnoDB), not a separate heap + pointer. Primary-key lookups skip the extra heap fetch - fewer I/Os per query.
-
Native S3-backed storage: tables/tablespace can live directly on S3, not just backups. Lets you decouple storage from compute, cuts local disk cost for cold/large tables. Big one for cloud-native setups like yours.
-
Lower WAL volume for updates: undo-log MVCC avoids duplicating full rows into WAL the way heap's
full_page_writesdoes on first touch after checkpoint. Less WAL = less replication lag, less network/storage cost on WAL shipping (relevant to your PG17→18 logical replication migration pattern). -
Copy-on-write checkpointing: smoother, less spiky I/O during checkpoints vs heap's periodic full fsync bursts.
-
Better multi-core write scalability: buffer manager/locking redesigned for modern many-core hardware; heap's lock contention under very high concurrent writes is a known bottleneck orioledb targets directly.
-
No more VACUUM FULL / pg_repack firefighting: undo-log cleanup handles bloat automatically; the manual bloat-remediation workflows DBAs run periodically become largely unnecessary.
Do you need to migrate ALL existing data?
No. It's not all-or-nothing. Two separate things are going on:
- The Postgres instance itself must be running the orioledb-patched binary (i.e. a cluster using the orioledb image) - this is instance-wide, one engine per running Postgres.
- But inside that instance, each table individually chooses its storage engine. Old-style tables (default heap) and new orioledb tables can coexist side-by-side in the same database. You pick per table:
CREATE TABLE my_table (...) USING orioledb; -- new engine
CREATE TABLE other_table (...); -- normal heap, default
sql
30 years of Postgres Innovation
Innovation in PostgreSQL has been happening silently for over 30 years. There are many specialized engines and extensions that can make Postgres more appealing and performant. In this blog post, we have tried to cover one such possibility which can improve the performance of your database without significant effort. OrioleDB can make it 2-5x faster than normal heap. If you are looking for additional help, do contact us. Our team is an official support partner for CloudNativePG. On this blog, you will find more posts related to PostgreSQL, CloudNativePG and more.
PostgreSQL Performance Optimization Services
If your PostgreSQL database is experiencing slow queries, high CPU or I/O utilization, table bloat, excessive VACUUM overhead, replication lag, or poor performance under concurrent workloads, CloudRaft can help identify and address the underlying bottlenecks.
Need help optimizing PostgreSQL performance?
Get expert help with PostgreSQL performance tuning, database optimization, query performance, table bloat, VACUUM and autovacuum optimization, and high-concurrency PostgreSQL workloads. CloudRaft helps teams build faster, more reliable PostgreSQL databases across cloud-native and Kubernetes environments.


