We have a running joke in the studio: whenever someone proposes a new database for a project, the conversation ends the same way. "Have you considered Postgres?" It's a joke because the answer is always yes, and the answer is almost always that Postgres is sufficient. Not just adequate — genuinely the best tool for the job in the overwhelming majority of cases we encounter.
This isn't dogma. We've shipped projects on MongoDB, DynamoDB, Redis, and SQLite. Each was the right choice for its context. But when a client asks "what database should we use?" without a specific constraint that points elsewhere, the answer is Postgres, and it has been for four years running. Here's why.
§ 01Why we default to Postgres
The core argument for Postgres is not any single feature. It's the combination of features that eliminates the need for additional infrastructure. Most applications need relational data, some semi-structured data, full-text search, background job queues, and maybe pub/sub for real-time updates. With any other database, that list requires three to five separate systems. With Postgres, it requires one.
One system means one backup strategy, one monitoring setup, one failure domain, one thing to upgrade, and one thing your team needs to understand. For teams of five or fewer engineers — which describes every client we work with — that consolidation is not a nice-to-have. It's the difference between a system they can operate and a system that operates them.
For a team of five, the best database is the one that eliminates the need for three other databases.
Postgres also has the most mature ecosystem of any open-source database. The tooling is deep: pgAdmin, psql, Prisma, Drizzle, PostgREST, pgBouncer, pg_dump, WAL-based replication. The hosting options are broad: AWS RDS, Supabase, Neon, Railway, Render, Fly.io, or self-hosted. The knowledge base is vast: almost any question you have has been answered, debugged, and optimized by someone in the last 28 years.
§ 02JSONB: the best of both worlds
The argument for document databases was always flexibility. "We don't know the schema yet, so we need something schemaless." This was a reasonable argument in 2015. It's not in 2026, because Postgres JSONB gives you document-style flexibility inside a relational database.
You can store arbitrary JSON in a jsonb column, query it with operators like ->> and @>, index it with GIN indexes for fast lookups, and validate it with CHECK constraints or JSON Schema validation. You get the flexibility of a document store and the guarantees of a relational database — transactions, foreign keys, joins — in the same row.
-- Store flexible metadata alongside structured data CREATE TABLE products ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL, price numeric(10,2) NOT NULL, metadata jsonb DEFAULT '{}'::jsonb ); -- Index the JSONB for fast queries CREATE INDEX idx_products_metadata ON products USING gin(metadata); -- Query nested JSON fields efficiently SELECT name, metadata->>'color' as color FROM products WHERE metadata @> '{"category": "electronics"}';
We use this pattern on almost every project. The core data model is relational — users, orders, products, invoices — with proper foreign keys and constraints. Metadata, feature flags, custom fields, and integration-specific data live in JSONB columns. The relational structure gives us integrity; the JSONB gives us flexibility. Neither compromises the other.
§ 03Full-text search without Elasticsearch
The second system most teams add alongside their primary database is a search engine — usually Elasticsearch or Algolia. For most products, this is unnecessary. Postgres full-text search handles everything up to about a million documents with sub-100ms query times, and it does it without any data synchronization pipeline.
The setup is straightforward: create a tsvector column, populate it with a trigger, add a GIN index, and query it with ts_query. You get ranked results, stemming, stop words, phrase matching, and boolean operators. It's not as feature-rich as Elasticsearch, but for product search, site search, or admin search, it's more than enough.
The advantage isn't just simplicity. It's consistency. When a product's name changes, the search index updates in the same transaction. There's no synchronization lag, no eventual consistency, no background job that might fail and leave the search index stale. The search results are always correct because they come from the same source of truth as the data.
If your search index lives outside your database, your search results are eventually wrong.
— Internal architecture principle
§ 04Postgres through Supabase
Supabase deserves its own section because it's changed how we think about Postgres in production. Supabase is, at its core, a managed Postgres instance with a set of well-built tools on top: a REST API (PostgREST), real-time subscriptions (via WAL), authentication, storage, and edge functions. It's not an abstraction over Postgres — it is Postgres, with direct SQL access and full control over the schema.
For client projects that need auth, real-time, and a database, Supabase eliminates two to three weeks of infrastructure setup. We've shipped three projects on Supabase in the last year, and in each case the time from "empty project" to "authenticated user reading real-time data from a normalized schema" was under a day.
The concerns we had early on — vendor lock-in, performance limits, migration difficulty — have not materialized. Because Supabase is just Postgres, migrating away means taking a pg_dump and restoring it on any other Postgres host. We've done this once when a client needed to move to AWS for compliance reasons, and it took an afternoon.
§ 05When NOT to use Postgres
Postgres is not always the answer. Here are the cases where we choose something else:
- Sub-millisecond key-value lookups at massive scale. If you're building a session store, a rate limiter, or a caching layer that needs to serve 100,000+ reads per second with sub-millisecond latency, use Redis. Postgres can handle high throughput, but it can't match Redis for this specific access pattern.
- Time-series data at high ingest rates. If you're ingesting millions of metrics per second — server telemetry, IoT sensor data, financial tick data — use TimescaleDB (which is a Postgres extension, interestingly) or ClickHouse. Vanilla Postgres will struggle with the write throughput and the time-bucketed query patterns.
- Graph traversals. If your primary access pattern is "find all nodes within N hops of this node" — social networks, recommendation engines, fraud detection — a graph database like Neo4j will outperform Postgres significantly. Recursive CTEs work for shallow graphs but fall apart at depth.
- Embedded or edge deployments. If the database needs to run on a mobile device, in a browser, or in a serverless function with minimal cold start, SQLite is the right choice. Postgres requires a server process.
- Multi-region write availability. Postgres replication is read-replica based. If you need writes to succeed in multiple geographic regions simultaneously with automatic conflict resolution, you need CockroachDB, Spanner, or a similar distributed SQL system.
Notice what's not on this list: "we need flexible schemas" (use JSONB), "we need search" (use pg full-text search), "we need real-time" (use LISTEN/NOTIFY or Supabase), "we need to scale" (Postgres handles millions of rows per table with proper indexing). The bar for choosing something other than Postgres is high, and it should be.
§ 06The stack we actually ship
For completeness, here's the database stack on a typical Invental project:
# Standard database stack Database: Postgres 16 (Supabase or Neon) ORM: Prisma or Drizzle (depends on project) Migrations: Prisma Migrate or raw SQL files Connection: pgBouncer in transaction mode Backups: Automated daily + WAL archiving Monitoring: pg_stat_statements + Grafana Search: pg full-text search (tsvector/tsquery) Jobs: pg-boss or Graphile Worker Real-time: Supabase Realtime or LISTEN/NOTIFY
One database. One backup strategy. One failure domain. One thing the client's team needs to understand after we hand off the project. That simplicity compounds over years, and it's the real reason Postgres keeps winning. Not because it's the best at any single thing, but because it's good enough at almost everything, and "good enough at everything in one system" beats "best-in-class across five systems" every time a small team has to maintain it at 2 AM.
— End of essay. Building something that needs a solid data layer? Start a project →