
9 min read
Feb 02, 2026
Table of contents
01 The “Use the Right Tool” Trap02 Why This Matters Now: The AI Era03 “But Specialized Databases Are Better!”04 The Hidden Costs Add Up05 The Modern Postgres Stack06 Quick Start: Add These Extensions07 Show Me the Code08 The Bottom Line09 Get Started10 Learn MoreThink of your database like your home. Your home has a living room, bedroom, bathroom, kitchen, and garage. Each room serves a different purpose. But they're all under the same roof, connected by hallways and doors. You don't build a separate restaurant building just because you need to cook. You don't construct a commercial garage across town just to park your car.
That's what Postgres is. One home with many rooms. Search, vectors, time-series, queues—all under one roof.
But this is exactly what specialized database vendors don't want you to hear. Their marketing teams have spent years convincing you to "use the right tool for the right job." It sounds reasonable. It sounds wise. And it sells a lot of databases.
Let me show you why it's a trap and why Postgres is the better choice in 99% of cases.
You’ve heard the advice: “Use the right tool for the right job.”
Sounds wise. So you end up with:
Congratulations. You now have seven databases to manage. Seven query languages to learn. Seven backup strategies to maintain. Seven security models to audit. Six sets of credentials to rotate. Seven monitoring dashboards to watch. And seven things that can break at 3 AM.
And when something does break? Good luck spinning up a test environment to debug it.
Here’s a different idea: Just use Postgres.
This isn’t just about simplicity. AI agents have made database sprawl a nightmare.
Think about what agents need to do:
With one database? That’s a single command. Fork it, test it, done.
With seven databases? Now you need to:
This is virtually impossible without a ton of R&D.
And it’s not just agents. Every time something breaks at 3 AM, you need to spin up a test environment to debug. With six databases, that’s a coordination nightmare. With one database, it’s a single command.
In the AI era, simplicity isn’t just elegant. It’s essential.
Let’s address this head-on.
The myth: Specialized databases are far superior at their specific tasks.
The reality: Sometimes they’re marginally better at a narrow task. But they also bring unnecessary complexity. It’s like hiring a private chef for every meal. Sounds luxurious, but it adds expense, coordination overhead, and creates problems you didn’t have before.
Here's the thing: 99% of companies don't need them. The top 1% have tens of millions of users and a large engineering team to match. You've read their blog posts about how amazing Specialized Database X works for them. But that's their scale, their team, their problems. For everyone else, Postgres is more than enough.
Here’s what most people don’t realize: Postgres extensions use the same or better algorithms as specialized databases (in many cases).
The “specialized database” premium? Mostly marketing.
What You Need | Specialized Tool | Postgres Extension | Same Algorithm? |
|---|---|---|---|
Full-text search | Elasticsearch | pg_textsearch | ✅ Both use BM25 |
Vector search | Pinecone | pgvector + pgvectorscale | ✅ Both use HNSW/DiskANN |
Time-series | InfluxDB | TimescaleDB | ✅ Both use time partitioning |
Caching | Redis | UNLOGGED tables | ✅ Both use in-memory storage |
Documents | MongoDB | JSONB | ✅ Both use document indexing |
Geospatial | Specialized GIS | PostGIS | ✅ Industry standard since 2001 |
These aren’t watered-down versions. They’re the same/better algorithms, battle-tested, open source, and often developed by the same researchers.
The benchmarks back this up:
Beyond the AI/agent problem, database sprawl has compounding costs:
Task | One Database | Seven Databases |
|---|---|---|
Backup strategy | 1 | 7 |
Monitoring dashboards | 1 | 7 |
Security patches | 1 | 7 |
On-call runbooks | 1 | 7 |
Failover testing | 1 | 7 |
Cognitive load: Your team needs SQL, Redis commands, Elasticsearch Query DSL, MongoDB aggregation, Kafka patterns, and InfluxDB’s non-native SQL workaround. That’s not specialization. That’s fragmentation.
Data consistency: Keeping Elasticsearch in sync with Postgres? You build sync jobs. They fail. Data drifts. You add reconciliation. That fails too. Now you’re maintaining infrastructure instead of building features.
SLA math: Three systems at 99.9% uptime each = 99.7% combined. That’s 26 hours of downtime per year instead of 8.7. Every system multiplies your failure modes.
These extensions aren’t new. They’ve been production-ready for years:
Over 48,000 companies use PostgreSQL, including Netflix, Spotify, Uber, Reddit, Instagram, and Discord.
The AI era brought a new generation:
Extension | Replaces | Highlights |
|---|---|---|
Pinecone, Qdrant | DiskANN algorithm. 28x lower latency, 75% less cost. | |
Elasticsearch | True BM25 ranking natively in Postgres. | |
External AI pipelines | Auto-sync embeddings as data changes. |
What this means: Building a RAG app used to require Postgres + Pinecone + Elasticsearch + glue code.
Now? Just Postgres. One database. One query language. One backup. One fork command for your AI agent to spin up a test environment.
Here’s all you need:
-- Full-text search with BM25
CREATE EXTENSION pg_textsearch;
-- Vector search for AI
CREATE EXTENSION vector;
CREATE EXTENSION vectorscale;
-- AI embeddings & RAG workflows
CREATE EXTENSION ai;
-- Time-series
CREATE EXTENSION timescaledb;
-- Message queues
CREATE EXTENSION pgmq;
-- Scheduled jobs
CREATE EXTENSION pg_cron;
-- Geospatial
CREATE EXTENSION postgis;That’s it.
Below are working examples for each use case. Skip to what you need.
The extension: pg_textsearch (true BM25 ranking)
What you’re replacing:
What you get: The exact same BM25 algorithm that powers Elasticsearch, directly in Postgres.
-- Create table
CREATE TABLE articles (
id SERIAL PRIMARY KEY,
title TEXT,
content TEXT
);
-- Create BM25 index
CREATE INDEX idx_articles_bm25 ON articles USING bm25(content)
WITH (text_config = 'english');
-- Search with BM25 scoring
SELECT title, -(content <@> 'database optimization') as score
FROM articles
ORDER BY content <@> 'database optimization'
LIMIT 10;SELECT
title,
-(content <@> 'database optimization') as bm25_score,
embedding <=> query_embedding as vector_distance,
0.7 * (-(content <@> 'database optimization')) +
0.3 * (1 - (embedding <=> query_embedding)) as hybrid_score
FROM articles
ORDER BY hybrid_score DESC
LIMIT 10;This is what Elasticsearch requires a separate plugin for. In Postgres, it’s just SQL.
The extensions: pgvector + pgvectorscale
What you’re replacing:
What you get: pgvectorscale uses the DiskANN algorithm (from Microsoft Research), achieving 28x lower p95 latency and 16x higher throughput than Pinecone at 99% recall.
-- Enable extensions
CREATE EXTENSION vector;
CREATE EXTENSION vectorscale CASCADE;
-- Table with embeddings
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT,
embedding vector(1536)
);
-- High-performance index (DiskANN)
CREATE INDEX idx_docs_embedding ON documents USING diskann(embedding);
-- Find similar documents
SELECT content, embedding <=> '[0.1, 0.2, ...]'::vector as distance
FROM documents
ORDER BY embedding <=> '[0.1, 0.2, ...]'::vector
LIMIT 10;Auto-sync embeddings with pgai:
SELECT ai.create_vectorizer(
'documents'::regclass,
loading => ai.loading_column(column_name=>'content'),
embedding => ai.embedding_openai(model=>'text-embedding-3-small', dimensions=>'1536')
);Now every INSERT/UPDATE automatically regenerates embeddings. No sync jobs. No drift. No 3 AM pages.
The extension: TimescaleDB (21K+ GitHub stars)
What you’re replacing:
What you get: Automatic time partitioning, compression up to 90%, continuous aggregates. Full SQL.
-- Enable TimescaleDB
CREATE EXTENSION timescaledb;
-- Create table
CREATE TABLE metrics (
time TIMESTAMPTZ NOT NULL,
device_id TEXT,
temperature DOUBLE PRECISION
);
-- Convert to hypertable
SELECT create_hypertable('metrics', 'time');
-- Query with time buckets
SELECT time_bucket('1 hour', time) as hour,
AVG(temperature)
FROM metrics
WHERE time > NOW() - INTERVAL '24 hours'
GROUP BY hour;
-- Auto-delete old data
SELECT add_retention_policy('metrics', INTERVAL '30 days');
-- Compression (90% storage reduction)
ALTER TABLE metrics SET (timescaledb.compress);
SELECT add_compression_policy('metrics', INTERVAL '7 days');The feature: UNLOGGED tables + JSONB
-- UNLOGGED = no WAL overhead, faster writes
CREATE UNLOGGED TABLE cache (
key TEXT PRIMARY KEY,
value JSONB,
expires_at TIMESTAMPTZ
);
-- Set with expiration
INSERT INTO cache (key, value, expires_at)
VALUES ('user:123', '{"name": "Alice"}', NOW() + INTERVAL '1 hour')
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
-- Get
SELECT value FROM cache WHERE key = 'user:123' AND expires_at > NOW();
-- Cleanup (schedule with pg_cron)
DELETE FROM cache WHERE expires_at < NOW();The extension: pgmq
CREATE EXTENSION pgmq;
SELECT pgmq.create('my_queue');
-- Send
SELECT pgmq.send('my_queue', '{"event": "signup", "user_id": 123}');
-- Receive (with visibility timeout)
SELECT * FROM pgmq.read('my_queue', 30, 5);
-- Delete after processing
SELECT pgmq.delete('my_queue', msg_id);Or native SKIP LOCKED pattern:
CREATE TABLE jobs (
id SERIAL PRIMARY KEY,
payload JSONB,
status TEXT DEFAULT 'pending'
);
-- Worker claims job atomically
UPDATE jobs SET status = 'processing'
WHERE id = (
SELECT id FROM jobs WHERE status = 'pending'
FOR UPDATE SKIP LOCKED LIMIT 1
) RETURNING *;The feature: Native JSONB
CREATE TABLE users (
id SERIAL PRIMARY KEY,
data JSONB
);
-- Insert nested document
INSERT INTO users (data) VALUES ('{
"name": "Alice",
"profile": {"bio": "Developer", "links": ["github.com/alice"]}
}');
-- Query nested fields
SELECT data->>'name', data->'profile'->>'bio'
FROM users
WHERE data->'profile'->>'bio' LIKE '%Developer%';
-- Index JSON fields
CREATE INDEX idx_users_email ON users ((data->>'email'));The extension: PostGIS
CREATE EXTENSION postgis;
CREATE TABLE stores (
id SERIAL PRIMARY KEY,
name TEXT,
location GEOGRAPHY(POINT, 4326)
);
-- Find stores within 5km
SELECT name, ST_Distance(location, ST_MakePoint(-122.4, 37.78)::geography) as meters
FROM stores
WHERE ST_DWithin(location, ST_MakePoint(-122.4, 37.78)::geography, 5000);The extension: pg_cron
CREATE EXTENSION pg_cron;
-- Run every hour
SELECT cron.schedule('cleanup', '0 * * * *',
$$DELETE FROM cache WHERE expires_at < NOW()$$);
-- Nightly rollup
SELECT cron.schedule('rollup', '0 2 * * *',
$$REFRESH MATERIALIZED VIEW CONCURRENTLY daily_stats$$);For AI applications, you often need both keyword search and semantic search:
-- Reciprocal Rank Fusion: combine keyword + semantic search
WITH bm25 AS (
SELECT id, ROW_NUMBER() OVER (ORDER BY content <@> $1) as rank
FROM documents LIMIT 20
),
vectors AS (
SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> $2) as rank
FROM documents LIMIT 20
)
SELECT d.*,
1.0/(60 + COALESCE(b.rank, 1000)) +
1.0/(60 + COALESCE(v.rank, 1000)) as score
FROM documents d
LEFT JOIN bm25 b ON d.id = b.id
LEFT JOIN vectors v ON d.id = v.id
WHERE b.id IS NOT NULL OR v.id IS NOT NULL
ORDER BY score DESC LIMIT 10;Try that with Elasticsearch + Pinecone. You’d need two API calls, result merging, failure handling, and double latency.
In Postgres: one query, one transaction, one result.
The extension: pg_trgm (built into Postgres)
CREATE EXTENSION pg_trgm;
CREATE INDEX idx_name_trgm ON products USING GIN (name gin_trgm_ops);
-- Finds "PostgreSQL" even with typo
SELECT name FROM products
WHERE name % 'posgresql'
ORDER BY similarity(name, 'posgresql') DESC;The feature: Recursive CTEs
-- Find all reports under a manager (org chart)
WITH RECURSIVE org_tree AS (
SELECT id, name, manager_id, 1 as depth
FROM employees WHERE id = 42
UNION ALL
SELECT e.id, e.name, e.manager_id, t.depth + 1
FROM employees e
JOIN org_tree t ON e.manager_id = t.id
WHERE t.depth < 10
)
SELECT * FROM org_tree;Remember the home analogy? You don't build a separate restaurant just to cook dinner. You don't construct a commercial garage across town just to park your car. You use the rooms in your home.
That's what we've shown you here. Search, vectors, time-series, documents, queues, caching—they're all rooms in the Postgres home. Same algorithms as the specialized databases. Battle-tested for years. Used by Netflix, Uber, Discord, and 48,000 other companies.
So what about that 99%?
For 99% of companies, Postgres handles everything you need. The 1%? That's when you're processing petabytes of logs across hundreds of nodes, or you need Kibana's specific dashboards, or you have exotic requirements that genuinely exceed what Postgres can do.
But here's the thing: you'll know when you're in the 1%. You won't need a vendor's marketing team to tell you. You'll have benchmarked it yourself and hit a real wall.
Until then, don't scatter your data across seven buildings because someone told you to "use the right tool for the right job." That advice sells databases. It doesn't serve you.
Start with Postgres. Stay with Postgres. Add complexity only when you've earned the need for it.
In 2026, just use Postgres.
All these extensions are available on Tiger Data. Create a free database in minutes:
psql "postgresql://user:pass@your-instance.tsdb.cloud.timescale.com:5432/tsdb"
CREATE EXTENSION pg_textsearch; -- BM25 search
CREATE EXTENSION vector; -- Vector search No need for specialized databases, just use Postgres.

10 Elasticsearch Production Issues (and How Postgres Avoids Them)
Jan 30, 2026
Why Elasticsearch is complex in production: garbage collection, shard math, data sync pipelines, and monitoring overhead. Postgres with pg_textsearch simplifies search.
Read more
Receive the latest technical articles and release notes in your inbox.