Back to Blog
postgresqldatabasearchitecturebackendpgvector

PostgreSQL Can Replace Your Whole Stack

With extensions for vector search, messaging, caching, and document storage — PostgreSQL has evolved from a database into a complete platform that replaces Redis, MongoDB, Elasticsearch, and more.

Aditya Vikram Mahendru6 min read
PostgreSQL Can Replace Your Whole Stack

The Database That Ate the World

PostgreSQL is no longer just a relational database. With its extension ecosystem, it has become a universal data platform that can replace half a dozen specialized services.

The pitch is simple: instead of running Postgres + Redis + MongoDB + Elasticsearch + RabbitMQ + a vector database, you can run just Postgres with extensions.

The Extension Ecosystem

pgvector — Vector Search

Semantic search, RAG embeddings, and recommendation engines without a separate vector database:

CREATE EXTENSION vector;

CREATE TABLE documents (
  id SERIAL PRIMARY KEY,
  content TEXT,
  embedding VECTOR(1536)  -- OpenAI embedding dimension
);

-- Search by semantic similarity
SELECT content, 1 - (embedding <=> '[0.01, -0.02, ...]') AS similarity
FROM documents
ORDER BY embedding <=> '[0.01, -0.02, ...]'
LIMIT 5;

-- With an IVFFlat index for speed
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops)
  WITH (lists = 100);

Replaces: Pinecone, Weaviate, Qdrant, Milvus

pg_cron — Scheduled Jobs

No more external cron daemons or scheduled workers:

CREATE EXTENSION pg_cron;

-- Vacuum every night
SELECT cron.schedule('nightly-vacuum', '0 3 * * *',
  'VACUUM ANALYZE');

-- Clean up expired sessions every hour
SELECT cron.schedule('cleanup-sessions', '0 * * * *',
  $$DELETE FROM sessions WHERE expires_at < NOW()$$);

-- Send digest emails daily
SELECT cron.schedule('daily-digest', '0 8 * * *',
  $$SELECT send_digest_emails()$$);

Replaces: cron, Sidekiq, Celery beat, AWS EventBridge

pg_later — Async Queries

Fire-and-forget queries that execute in the background:

SELECT pg_later.submit($$REFRESH MATERIALIZED VIEW analytics_mv$$);
SELECT pg_later.submit(
  $$UPDATE users SET reputation = compute_reputation(id) WHERE id = $1$$,
  ARRAY['123e4567']
);

Replaces: Background job queues, Celery, BullMQ

pgmq — Message Queues

A full-featured message queue inside Postgres:

CREATE EXTENSION pgmq;

-- Create a queue
SELECT pgmq.create('email_queue');

-- Send messages
SELECT pgmq.send('email_queue',
  '{"to": "user@example.com", "subject": "Welcome!"}');

-- Receive (with visibility timeout)
SELECT pgmq.read('email_queue', 60, 5);

-- Delete after processing
SELECT pgmq.delete('email_queue', msg_id);

-- Archive for audit
SELECT pgmq.archive('email_queue', msg_id);

Replaces: RabbitMQ, Redis pub/sub, SQS, NATS

pg_analytics — Columnar Storage

Analytics queries on Postgres data with DuckDB-compatible performance:

CREATE EXTENSION pg_analytics;

-- Convert a table to columnar format
ALTER TABLE events SET ACCESS METHOD columnar;

-- Run OLAP queries at columnar speed
SELECT DATE_TRUNC('day', created_at), COUNT(*), AVG(revenue)
FROM events
GROUP BY 1
ORDER BY 1 DESC;

Replaces: ClickHouse, Snowflake, Redshift (for many workloads)

PostGIS — Geospatial

The gold standard for spatial data:

CREATE EXTENSION postgis;

SELECT name, ST_Distance(
  location,
  ST_SetSRID(ST_MakePoint(-73.985, 40.748), 4326)
) AS distance
FROM landmarks
ORDER BY location <-> ST_SetSRID(ST_MakePoint(-73.985, 40.748), 4326)
LIMIT 10;

Replaces: MongoDB GeoJSON, Elasticsearch Geo, dedicated GIS servers

hstore + JSONB — Document Store

NoSQL-style flexible schemas alongside relational data:

CREATE TABLE products (
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL,
  attributes JSONB,
  metadata HSTORE
);

-- Index any JSON path
CREATE INDEX ON products USING GIN (attributes jsonb_path_ops);

-- Query deep into documents
SELECT * FROM products
WHERE attributes @> '{"category": "electronics", "specs": {"wifi": "6E"}}';

Replaces: MongoDB, Firebase, DynamoDB (for document workloads)

Putting It Together

The Stack Before

flowchart LR
  A[Client] --> B[Next.js]
  B --> C[Redis Cache]
  B --> D[Postgres DB]
  B --> E[Elasticsearch]
  B --> F[RabbitMQ]
  B --> G[Pinecone]
  B --> H[MongoDB]

Six infrastructure services to manage, backup, monitor, and scale.

The Stack After

flowchart LR
  A[Client] --> B[Next.js]
  B --> C[Postgres + Extensions]

One database. One backup strategy. One monitoring dashboard.

The Consolidation Map

ServiceExtensionsProtocol
MongoDBjsonb, btree_ginNative JSON + GIN indexes
Redis Cachepg_bm25, pg_lruBuilt-in caching + BM25 search
Redis Pub/Subpgmq, LISTEN/NOTIFYMessage queue + event notifications
RabbitMQpgmqPersistent, SQL-managed queues
Elasticsearchpgvector, pg_bm25Full-text + vector search
PineconepgvectorVector similarity with IVFFlat / HNSW
Sidekiq / Celerypg_cron, pg_laterScheduled + background queries
ClickHousepg_analyticsColumnar storage for OLAP
Neo4jageGraph queries (Apache AGE)
Firebasejsonb + PostgRESTREST API from Postgres schemas

When to Consolidate and When Not To

Consolidate When

  • Team size is small — fewer services = less ops burden
  • Consistency matters — ACID transactions across features
  • Latency tolerance — all-in-one is fast but not Redis/RabbitMQ fast
  • Deployment simplicity — one Docker image vs. six
  • Startup / side project — move fast, extract later

Keep Separate When

  • Throughput exceeds Postgres limits — 10k+ msg/s queues belong in RabbitMQ
  • Sub-millisecond cache required — Redis is still faster for hot keys
  • Elasticsearch for full-text — pg_bm25 is good, ES is better at scale
  • Compliance / isolation — separate services for regulatory requirements

Real World: My Cluster

On my K3s cluster, Postgres with these extensions powers:

# postgres.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  template:
    spec:
      containers:
      - name: postgres
        image: pgvector/pgvector:pg17
        env:
        - name: POSTGRES_EXTENSIONS
          value: "vector,pg_cron,pgmq,postgis,hstore,pg_analytics"

Running in a single 4GB LXC container — it serves as:

  • Primary application database
  • Full-text search engine (via GIN + pg_bm25)
  • Vector store for AI embeddings
  • Background job queue (pgmq)
  • Scheduled task runner (pg_cron)
  • Analytics store (columnar via pg_analytics)
  • Cache layer (materialized views + LISTEN/NOTIFY)

Caveats

  • Postgres replication is single-primary — you need a connection pooler (PgBouncer) for high concurrency
  • Extensions must be installed per-database
  • Not all extensions are available on managed Postgres (RDS, Cloud SQL)
  • Write throughput tops out around 50k-100k writes/second on a single node

Conclusion

PostgreSQL's extension ecosystem has quietly transformed it from a relational database into an operating system for data. For most applications — especially startups, side projects, and internal tools — a single Postgres instance with the right extensions can replace your entire data infrastructure.

Fewer services means fewer failure points, simpler deployments, and more time building features instead of managing databases.