Engineering · 2026-05-16 · By RedDB team · 10 min read

Stop stitching Postgres, pgvector, S3, and Redis

Every modern app you ship is four databases in a trenchcoat. Here is the bill — in failure surfaces, ops playbooks, and consistency models — that nobody costs out before signing.

The default stack for a serious application in 2026 looks like this: Postgres for rows, pgvector or a sidecar like Qdrant for embeddings, S3 (or R2, or MinIO) for blobs, Redis for the hot path. Four engines. Four query languages, if you count “S3 GET” as a query language. Four sets of credentials, four backup strategies, four ways the system can be down on a Saturday night.

You did not pick this stack. The stack picked you. Each engine arrived for a defensible reason: Postgres because it is the safe boring choice and you needed transactions; pgvector because the embeddings had to go somewhere and the Postgres extension was easier than spinning up another service; S3 because the PDFs and audio clips would not fit in a row; Redis because somebody put SELECT inside a tight loop and the read replicas could not keep up.

Each decision was correct in isolation. The aggregate is what is killing your team.

The four bills you are paying

When people compare databases, they compare benchmarks. Throughput, P99, cost per gigabyte. The benchmarks miss the actual line item, which is paid in engineer-hours and pager fatigue, not microseconds.

Pull up the last twelve months of incidents on any team running the four-engine stack and you will find the same pattern: most of the outages live in the seams. The database is fine. The cache is fine. The blob store is fine. What broke is the assumption that all three agreed about the state of the world at the same moment.

Here is the bill, in four lines.

Bill #1: Four failure surfaces. Postgres has its own failover semantics. Redis has its own (and it is not the same one). S3 has eventual consistency on some operations and strong consistency on others depending on the year and the vendor. pgvector inherits Postgres but its index build is its own beast. Each of these has its own way of paging you at 3am, its own runbook, its own “yes we have seen this before” tribal knowledge. The number of distinct ways the system can be in a bad state is roughly the product, not the sum.

Bill #2: Four consistency models. Postgres gives you snapshot isolation by default. Redis gives you “whatever was in memory until the last fsync, and trust us about the fsync.” S3 gives you read-after-write for new objects and eventual consistency for overwrites (and there is a footnote per region). pgvector gives you Postgres semantics for the rows and an HNSW graph that does not participate in your transaction at all. Stitching these together means writing application code that pretends one consistency model exists. That code is wrong. You just have not hit the bug yet.

Bill #3: Four ops playbooks. Postgres needs pg_upgrade rehearsals, replica lag dashboards, autovacuum tuning. Redis needs eviction policy choices, persistence tradeoffs, cluster reshard procedures. S3 needs lifecycle rules, multipart upload retries, bucket policy audits. pgvector needs index rebuild plans when you change the embedding model. Each is a binder of operational knowledge. New hires read four binders before they are useful on call. Senior engineers maintain four binders or, more honestly, two of them and a wiki page that says “ask Maria.”

Bill #4: Four authentication and authorization surfaces. Postgres roles, Redis ACLs, S3 IAM policies, and whatever you bolted on for the vector store. Each system has its own concept of “user,” “permission,” and “audit log.” Centralizing this is its own project. Most teams do not centralize it; they accept the sprawl and hope the next compliance audit is lenient.

None of these bills show up in the database vendor’s pricing page.

The integration tax

The four engines do not talk to each other. Your application is the integration layer. Every interesting business operation is therefore a distributed transaction, and most teams handle distributed transactions by pretending they do not have them.

Consider the simplest possible AI feature: a user uploads a PDF, the system extracts text, generates embeddings, and makes the document searchable. On the four-engine stack:

  1. Write metadata row to Postgres.
  2. Upload PDF to S3.
  3. Push extraction job to Redis queue.
  4. Worker pulls job, fetches PDF from S3, computes embeddings.
  5. Worker writes embeddings to pgvector.
  6. Worker updates Postgres row with status = 'indexed'.

How many of those steps can fail independently? Six. How many of those failures leave the system in a consistent state on their own? Zero. Every team I have talked to has at some point shipped a version of this with a bug where the row says indexed but the embeddings are missing, or the embeddings exist but the row was rolled back, or the S3 object was uploaded but the row never got written and the orphan sits in a bucket until somebody runs a reconciliation script that turns out to also have a bug.

You can fix this. The fixes are real engineering work. You write idempotent workers. You design compensating transactions. You build a reconciler. You write a runbook for “what to do when the embeddings job dies between steps 4 and 5.” You staff a team to maintain the reconciler.

Or you write one transaction.

“But Postgres can do all of this”

Yes, sort of. This is the response I expect from anyone who has shipped on Postgres + pgvector + lo (large objects) and got it working. The shape of the response is: “we run one engine, we did not have to integrate anything, your manifesto is invalid.”

The honest version of that argument: Postgres handles the document and vector path well, lo is fine for moderate blobs, and a queue can live in a table. This is true at small scale and for some workloads. We wrote a whole companion post titled When you don’t need RedDB saying so.

Here is where the argument breaks. The moment your blobs are large enough that you do not want them in the WAL, you move them to S3. The moment your embedding workload outgrows pgvector’s index build window, you spin up Qdrant or Weaviate next to it. The moment your queue throughput needs durability semantics that a Postgres table cannot give you cheaply, you reach for Redis Streams or Kafka. The single-engine stack is stable until the workload changes, and then it pulls itself apart engine by engine and you are back to four.

The four-engine stack is not a choice. It is the stable equilibrium that Postgres-only teams drift into.

What “small team unlock” means

The common framing of multi-model databases is “scale.” You hear “consolidated storage” and assume the pitch is about saving infrastructure cost at high scale.

That framing is wrong, or at least incomplete. The bigger benefit is at small scale.

A team of three engineers cannot run four engines in production responsibly. They can run them; the question is whether they can run them well. A four-engine stack assumes you have someone who knows Postgres replication, someone who has been paged for a Redis OOM at least three times, someone who has fought S3 IAM, and someone who has rebuilt a pgvector index after switching embedding models. At three engineers, this person is you, and you have to be all four people in a single brain. That is the actual cost.

At fifteen engineers, you have specialists, but now the specialists have created silos. The team that owns the vector store does not own the source-of-truth row in Postgres. When the embeddings get stale, ownership of the bug is unclear. The drift window — the time between source data changing and embeddings being recomputed — becomes a customer-visible bug surface and a political question about which team’s queue takes priority.

A multi-model engine collapses that. One transaction. One ownership boundary. The reconciler does not exist because the inconsistency it reconciles does not exist.

We will go deep on the ops cost math in a follow-up: Why best-of-breed loses at small scale. The short version is that engineer-hours dominate infrastructure cost at every team size you are likely to operate at, and engineer-hours scale with the number of distinct systems you have to keep alive.

The performance argument is the weakest one

Best-of-breed proponents will note that a purpose-built vector store will beat a multi-model engine on raw vector search benchmarks. They are right. We do not pretend otherwise — read our own tradeoffs post for what we gave up to fit four engines under one transaction.

But this is the wrong axis to fight on. The performance argument is the weakest argument for the four-engine stack, because the performance gap closes every quarter and the operational gap does not. HNSW implementations are converging. JSON indexing in row stores is good enough for most document workloads. The blob path is the blob path. What does not converge is the cost of running four systems versus one. That cost is structural. It comes from the integration layer being your application code, which is to say, code your team writes and maintains.

If your vector search workload genuinely needs the last 15% of throughput that a single-purpose store provides, run a single-purpose store. We will tell you to. But “we benchmark 15% slower on synthetic vector recall” is a strange reason to accept four times the on-call burden.

What we are actually arguing

The argument is not “RedDB is faster than your four engines.” Sometimes it is, often it is not. The argument is that the cost of stitching has been quietly moved off the database vendor’s bill and onto your team’s calendar, and that move is not free.

You are paying for the integration layer in code your team writes, in incidents that pull people away from features, in onboarding ramps that take twice as long because there are twice as many systems to learn, in audit findings that say “your access control surface is not consistent across data stores.”

A multi-model engine is the bet that those costs are higher than the cost of giving up some per-modality tuning. For the teams we have worked with — and especially for the teams of two-to-fifteen who are shipping product, not running infrastructure — that bet pays off. For teams running specialized workloads at massive scale with a dedicated platform org, it may not. Honesty about which side of the line you are on is the whole point.

If you are on the four-engine treadmill and the last incident you handled lived in the seams between two of them, the cost of stitching is not theoretical for you. It is the meeting you canceled this week because the reconciler was paging again.

We built RedDB because we wanted to stop having that meeting.

Where to go from here

This is the anchor of a series. Over the next few weeks we will go deeper on each face of the argument:

If you want to see what a single transaction across document, vector, KV, and blob actually looks like, the RAG without a second database post has the schema and the code.

The first step is to count, on your current stack, how many of your last ten incidents lived in the seams. If the answer is more than two, you already know what this post is about.

© 2026 RedDB.io. AGPL-3.0 self-host · Managed Cloud.