What 11 PostgreSQL scaling stories taught me
I've been collecting first-hand "how we scaled" posts from engineering blogs, and PostgreSQL shows up more than any other database: 11 stories from 7 companies so far. Read them back to back and a pattern jumps out. Almost nobody starts by sharding. They squeeze a lot out of one database first, then split by domain, and only shard when they really have to.
Here's what each story adds, in roughly the order you'd hit these problems.
1. One primary can go a very long way
The most surprising entry is also the newest. In Scaling PostgreSQL to power 800 million ChatGPT users (January 2026), OpenAI describes running ChatGPT on a single-primary Postgres with nearly 50 global read replicas, serving millions of queries per second.
The trick isn't one clever feature. It's a stack of protections around that primary:
- reads are pushed to replicas,
- write-heavy workloads that can be sharded move to Cosmos DB instead of landing on Postgres,
- PgBouncer handles connection pooling,
- cache leasing and multi-layer rate limits stop traffic spikes from reaching the database.
My takeaway: before you design a sharding scheme, ask which workloads actually need to live on the primary. Moving the shardable, write-heavy ones somewhere else can buy you years.
2. Connections are a resource, so pool them
Connection count is a quiet limit that bites early. Two stories lean on PgBouncer (OpenAI above, and Notion's re-shard below), and Supabase went further and built its own pooler.
In Supavisor: Scaling Postgres to 1 Million Connections (2023), Supabase load-tested Supavisor, a Postgres pooler written in Elixir, with one million client connections at 20,000 QPS. Keep in mind this is a load test, not a production incident report, but it shows how far you can push the "many clients, few real connections" model.
3. Watch for small features with big side effects
Why we spent the last month eliminating PostgreSQL subtransactions is my favourite kind of post: a whole month spent on something that looks tiny. GitLab traced stalls on its read replicas to Postgres subtransaction overflow, and the fix was removing subtransactions from its code.
The lesson I take from it: if your replicas lag or stall under load and nothing obvious changed, look at what your ORM or app code is doing per transaction, not just at hardware.
4. Split by domain before you shard
Two teams solved "one database is too busy" without sharding at all:
- Figma, in The growing pains of database architecture (2023), moved from one Postgres instance to several databases by vertically partitioning groups of tables, alongside read replicas and connection pooling.
- GitLab, in We are splitting our database into Main and CI (2022), moved its CI tables into their own Postgres database to take load off the main one.
Vertical partitioning works when your tables naturally fall into groups that rarely join each other. It's usually less risky than sharding because each group still lives in one ordinary database.
5. When you do shard, pick the key your product already has
Eventually Figma and Notion both had to shard horizontally.
- In Herding elephants (2021), Notion split its Postgres monolith into a fleet of logical shards keyed by workspace, migrating with double writes and backfills.
- In How Figma's Databases Team Lived to Tell the Scale (2024), Figma describes a nine-month effort built on a custom query proxy, with logical sharding before physical splits.
That "logical first" idea is worth stealing. If your app already routes queries by shard key while the data still sits in one place, the physical move becomes a separate, smaller project.
6. Plan for sharding twice
Sharding isn't one and done. In The Great Re-shard (2023), Notion tripled its Postgres fleet by re-sharding with logical replication and careful PgBouncer cutovers, with no downtime.
If you shard, design for moving data between shards from day one, because you'll need to.
7. The replication log is an API
Two stories treat Postgres replication as more than a backup:
- Figma's LiveGraph (2021) tails the Postgres replication log so the UI can subscribe to live query results.
- Notion's re-shard (above) used logical replication to copy data into new shards.
Once you see the log as a stream you can build on, real-time features and migrations get a lot easier.
8. Shape the data for how it's queried
How We Scaled PostgreSQL to 350 TB+ (2023) is Timescale using its own Postgres extension for a database-observability product. The dataset is 350 TB+ with 10 billion new records a day, kept queryable with hypertables (time-based partitioning), columnar compression and tiered storage.
For append-heavy time-series data, partitioning by time plus compressing and tiering older data is often a better move than adding more servers.
9. Sometimes the scale is the number of databases
The last story flips the usual problem. In Migrating the Jira Database Platform to AWS Aurora (2025), Atlassian moved about four million Jira databases from RDS Postgres to Aurora, weighing reliability, downtime and cost along the way.
When you run a database per tenant, the hard part is fleet automation, not query tuning.
The pattern, in one list
If I had to turn these 11 stories into a checklist:
- Offload reads to replicas and move shardable write-heavy work off the primary.
- Pool connections early.
- Profile what your app does inside transactions.
- Split table groups into separate databases before sharding.
- Shard by the tenant key your product already has, logically first.
- Build for re-sharding.
- Use logical replication for migrations and real-time features.
- Partition, compress and tier time-series data.
None of this is new on its own. What's useful is seeing which steps real teams took, and in what order.
All 11 stories, with stack, techniques and the scale numbers each post states, are on one page: How teams scaled PostgreSQL. If you know a good first-hand Postgres scaling post I've missed, I'd love to hear about it in the comments.
Comments
Join the discussionNo comments yet. Be the first to comment.